Expressions vs substitutions (newbie question)

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

Expressions vs substitutions (newbie question)

remerson
Hi,

I'm just starting out with Thymeleaf and I really like it, but I'm confused by an apparent issue around string substitution/interpolation.

This works:
  <p th:text="'Hello ' + ${name}">Hello prototype</p>
But this doesn't (it gives the error "Could not parse as expression"):
  <p th:text="Hello ${name}">Hello prototype</p>
So it looks like the value of the th:text attribute has to be an expression which evaluates to a string, instead of an actual string with possible substitutions/interpolations in it.  This seems strange, because the content of the 'p' element can't possibly be anything other than a string.

Furthermore, if the value of the th:text attribute is actually an expression, why is it necessary to say ${name} to refer to the variable called 'name'?  That variable should be in scope already, so why the curly brackets?

AFAIK the usual meaning of ${} is a substitution/interpolation which can appear anywhere in a string (e.g. in Maven, Spring etc).  Is it a different meaning in Thymeleaf, or is this a SpEL thing... or have I just totally misunderstood what's going on?  ;)

Thanks in advance for your help,
Richard
Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

Zemi
Administrator
> So it looks like the value of the th:text attribute has to be an expression which evaluates to a string,
> instead of an actual string with possible substitutions/interpolations in it.

Yes, that's the way it works.

> This seems strange, because the content of the 'p' element can't possibly be anything other than a string.

If the content of the element is HTML instead of plain text, you have to use th:utext:

     <p th:utext="${formattedHtml}">...</p>

> Furthermore, if the value of the th:text attribute is actually an expression, why is it necessary
> to say ${name} to refer to the variable called 'name'?  That variable should be in scope already, 
> so why the curly brackets?

I think this is because in Thymeleaf there are different types of expressions: ${...}, @{...}, #{...}

Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

remerson
> Yes, that's the way it works.

But, I can also do something like this:
  th:text="${agent.hostname + ':' + agent.port}"

If the whole value of the th:text attribute is an expression, and the ${} is a reference to a variable, then "${agent.hostname + ':' + agent.port}" should refer to a single variable whose name has a ':' in the middle.  But it actually evaluates the agent.hostname and agent.port variable references separately, then concatenates their values (together with the ':').

This seems rather confusing.  What does the ${} really mean?

I've had a look through all the documents but I can't find a detailed description of the expression language.

Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

Zemi
Administrator
The is a Thymeleaf native syntax which allows doing things like

    <div th:text="#{foo} + ' ' + ${bar} + ' ' + @{baz}">...</div>

On the other hand, ${...} evaluates the expression inside using ONGL o SPeL expression language.

Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

remerson
I think that's the part I don't understand.  Why is this a good idea:

  <div th:text="#{foo} + ' ' + ${bar} + ' ' + @{baz}">...</div>

when it would be so much easier and clearer to just do this:

  <div th:text="#{foo} ${bar} @{baz}">...</div>

Certainly all the things inside those {}s could and should be expressions in OGNL or SpEL.  But it doesn't seem sensible (to me) to treat the stuff outside the {}s as an expression -- at least not for attributes where the whole point is to generate some text.

I guess this way you don't have to remember which attributes take a real expression (e.g. th:each, whose value should therefore not involve any {}) and which ones take a string value (e.g. th:text).  I'm not convinced that's a good tradeoff, but, well, I haven't had much experience of Thymeleaf yet ;)

I wonder how easy it would be to make a "modified standard" dialect in which some attribute values are interpreted as text (with possible {}-wrapped expressions inside) and some are treated as expressions (with no need for {}).  It might be worth a try...
Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

Zemi
Administrator
> I think that's the part I don't understand.  Why is this a good idea:

I'm sorry, I know nothing about the design decisions, I can't help you about that.
Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

remerson
Thanks for your help though Zemi -- I definitely have a better understanding of what is going on now :)

I wonder if anyone else can help us to understand the design decision here...?

Also, I think it would be helpful if the reference documentation could be more explicit about the syntax and semantics of expressions in Thymeleaf.  Is the documentation maintained as part of the source repository?
Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

Emanuel
Administrator
I can see where you're coming from with your questions, especially the ${variable1 + ' ' + variable2} one, having become used to how Gradle/Groovy strings and the ${} in those work.  It might be an combination OGNL/SPEL and Thymeleaf thing, seeing that OGNL/SPEL can interpret everything we put inside the ${}, and Thymeleaf can then concatenate everything after that.

Daniel, the creator behind Thymeleaf, would be your best bet for answering such questions, including the documentation one.  He visits the forums semi-regularly, so he might have a better answer (instead of my "oh, it's OGNL's fault :) "), for you.
Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

danielfernandez
Administrator
Hi!

The reason this:

    th:text="${oneVar} ${twoVar}"

...does not work is because Standard Expressions in thymeleaf are a bit richer than merely executing expressions inside a String.

For example, you can have conditionals like this one, which would output the vale of ${twoVar} if ${oneVar} is true, and ${threeVar} if false:

    th:text="${oneVar} ? ${twoVar} : ${threeVar}"

...or things like this, which would output ${oneVar} unless it's null, in which case it would output ${twoVar}:

    th:text="${oneVar} ?: ${twoVar}"

And things can get more complicated, because we can mix types of expressions and even nest:

    th:text="${oneVar} ?: (${twoVar} ? #{three.message} : @{four.url})"

And if your expressions get too big, you can add line feeds and indent:

    th:text="${oneVar} ?:
                    (${twoVar} ?
                        #{three.message}
                      : @{four.url})"


The downside to this is there is no real possibility to understand the difference between a whitespace that should go straight into the result and a whitespace that is there simply to separate ? and :, so we cannot have the kind of text-appending capability you'd like to have :-(

See more about the Standard Syntax here: http://www.thymeleaf.org/standarddialect5minutes.html

Future versions of thymeleaf might create some sort of "literal expression" that disables conditionals, defaults, etc. when required, in order to not need those "+" appending operations. For example, something like using "|" symbols this way:

    th:text="|${oneVar} ${twoVar}|"

Any volunteers to create a feature request ticket at GitHub? ;-)

Regards,
Daniel.
Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

remerson

Ok, I understand those examples, but I'm still not really clear on what exactly ${} means.  Can anyone describe it exactly?

Also, I think all those examples could be written more simply and still work just as well:

  th:text="${oneVar} ? ${twoVar} : ${threeVar}"
  th:text="${oneVar ? twoVar : threeVar}"

  th:text="${oneVar} ?: ${twoVar}"
  th:text="${oneVar ?: twoVar}"

Are there really different types of expression, or just different transformations applied to a value?

  th:text="${oneVar} ?: (${twoVar} ? #{three.message} : @{four.url})"
  th:text="${oneVar ?: (twoVar ? #three.message : @four.url)}"
  or even  th:text="${oneVar ?: (twoVar ? three.#message : four.@url)}"

It seems to me that for the value of an attribute like th:text, it's not necessary or desirable for the whole attribute value to be treated as an expression.  If any operators etc are needed, they can be used in an expression inside ${}.  That's what ${} should mean: 'here is an expression'.  Anything outside the ${} is just text.

I think that defining ${} like that would be much more consistent with how ${} is used in other tools/frameworks.

But like I said earlier, it might mean that some attributes like th:text would have a value which is treated as text (with possible ${}s inside), because their purpose is to generate text.  But other attributes like th:each, th:if etc would have a value which is treated as an expression (and not surrounded by ${}), because their purpose is to generate a collection or a boolean or whatever.

Such an inconsistency might be confusing for people, but it could be made clearer by splitting the standard dialect into two halves: one half for "text attributes", the other half for "expression attributes", so you can easily see what to use for any given attribute by looking at its namespace.

Anyway I'm just throwing in some ideas.  Please don't take these as criticisms - I think Thymeleaf is great :)
Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

danielfernandez
Administrator
Hi,

Please read http://www.thymeleaf.org/standarddialect5minutes.html Most of your questions are explained there, and also at the "Using Thymeleaf" tutorial.

A "Thymeleaf Standard Expression" is everything between "'s in something like a th:text attribute.

Thymeleaf Standard Expressions can contain inside:

    ${...} and *{...} <-- "variable" expressions, which are executed by an expression language. By default this is OGNL if you are not using Spring, and Spring EL if you are. If you create your own thymeleaf dialects, you could use other expression languages of your choice.

    #{...} <-- "text internationalization/externalization" expressions, which represent internationalization messages (coming from a "MessageSource" if you are using Spring)

    @{...} <-- "link" expressions, which perform the adequate URL rewriting and parameter encoding operations on the data you specify for them.

All these kinds of component expressions can be used in the same "Thymeleaf Standard Expression", and interact among themselves by means of the Thymeleaf Standard Expression operators, including conditional evaluation etc. But think that the parts inside "${...}" are only evaluated by OGNL or SpringEL and not thymeleaf, whereas the "?", ":", "?:", etc. outside the brackets are evaluated by thymeleaf itself.

So in order to allow: "${oneVar ? twoVar : threeVar}" this should be implemented by OGNL or SpringEL, not by Thymeleaf. Because in this case the entire "oneVar ? twoVar : threeVar" string would be passed to these expression engines as the expression to be executed.


Regards,
Daniel.
Reply | Threaded
Open this post in threaded view
|

Re: Expressions vs substitutions (newbie question)

remerson

Thanks for your response and clarification.

If I get a chance (it could be a while!), I'll try to implement a rough sketch of an alternative dialect which does things the way I'm suggesting, to see if it helps to make templates clearer (or who knows, it might make them worse...)

Cheers everyone and thanks for your help :)

Richard