Follow these steps to create a simple Hello World template and use it in Java:
-
Create a new Maven project using your favorite IDE. (Soy is a plain Java library and will work with any Java build tool. This example uses Maven.) The final directory structure will look like this:
. ├── pom.xml └── src └── main └── java └── example └── HelloWorld.java └── simple.soy
-
All files that contain Soy end with the
.soy
file extension and are called Soy files. Createsrc/main/java/example/simple.soy
containing the following line:{namespace examples.simple}
This line declares a namespace for all the templates that you define in this file.
-
Copy the following template to
src/main/java/example/simple.soy
, making sure that it appears after the namespace declaration:{template .helloWorld} Hello world! {/template}
This template simply outputs the text
Hello world!
. It has the partial name.helloWorld
, which, when combined with the namespace, forms the fully qualified template nameexamples.simple.helloWorld
. -
Now that we've written the template, we need to write the Java code to render the template. To do that, we need to tell Maven to add a dependency on Soy.
pom.xml
is the main Maven configuration file. Edit to add the dependency:<dependencies> <dependency> <groupId>com.google.template</groupId> <artifactId>soy</artifactId> <version>2018-03-14 </version> <!-- Or latest version.--> </dependency> </dependencies>
(See here for more information about the structure of
pom.xml
.) -
Create
src/main/java/example/HelloWorld.java
with the following contents:package example; import com.google.template.soy.SoyFileSet; import com.google.template.soy.tofu.SoyTofu; public class HelloWorld { public static void main(String[] args) { SoyFileSet sfs = SoyFileSet .builder() .add(HelloWorld.class.getResource("simple.soy")) .build(); SoyTofu tofu = sfs.compileToTofu(); System.out.println( tofu.newRenderer("examples.simple.helloWorld").render()); } }
This example bundles the template files that you specify (in this case, just
simple.soy
) into aSoyFileSet
object, then compiles the bundle into aSoyTofu
object with a call tocompileToTofu()
. The final line calls the template, using the template's fully qualified nameexamples.simple.helloWorld
, and renders its output to standard out. -
Add the following snippet to
pom.xml
to tell Maven how to execute the main class (HelloWorld
):<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.6.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>java</goal> </goals> </execution> </executions> <configuration> <mainClass>example.HelloWorld</mainClass> </configuration> </plugin> </plugins> </build>
-
Run
mvn package
at the root of your project. You should see this message at standard out:Hello world!
-
Add the following second template, called
.helloName
, tosimple.soy
. Note that.helloName
takes a required parameter calledname
, which is declared by@param
. It also takes an optional parametergreetingWord
, which is declared by@param?
. These parameters are referenced in the template body using the expressions$name
and$greetingWord
, respectively. This template also demonstrates that you can conditionally include content in templates via theif-else
commands. You can put this template before or after the.helloWorld
template, just as long as it's after thenamespace
declaration./** Greets a person using "Hello" by default. */ {template .helloName} {@param name: string} /** The person's name. */ {@param? greetingWord: string} /** * Optional greeting word to use * instead of "Hello". */ {if not $greetingWord} Hello {$name}! {else} {$greetingWord} {$name}! {/if} {/template}
-
Add a third template to the file. This template,
helloNames
, demonstrates afor
loop with anifempty
command. It also shows how to call other templates and insert their output using thecall
command. Note that thedata="all"
attribute in thecall
command passes all of the caller's template data to the callee template./** Greets a person and optionally a list of other people. */ {template .helloNames} {@param name: string} /** The person's name. */ {@param additionalNames: list<string>} /** * Additional names to greet. * May be an empty list. */ // Greet the person. {call .helloName data="all" /}<br> // Greet the additional people. {for $additionalName in $additionalNames} {call .helloName} {param name: $additionalName /} {/call} {if not isLast($additionalName)} <br> // break after every line except the last {/if} {ifempty} No additional people to greet. {/for} {/template}
-
Now edit
src/main/java/exampleHelloWorld.java
to call the new templates and exercise them with data:package example; import com.google.template.soy.SoyFileSet; import com.google.template.soy.tofu.SoyTofu; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class HelloWorld { public static void main(String[] args) { SoyFileSet sfs = SoyFileSet .builder() .add(HelloWorld.class.getResource("simple.soy")) .build(); // helloWorld SoyTofu tofu = sfs.compileToTofu(); System.out.println( tofu.newRenderer("examples.simple.helloWorld").render()); // For convenience, create another SoyTofu object that has a // namespace specified, so you can pass partial template names to // the newRenderer() method. SoyTofu simpleTofu = tofu.forNamespace("examples.simple"); // helloName Map<String, Object> data = new HashMap<>(); data.put("name", "Ana"); System.out.println("-----------------"); System.out.println( simpleTofu.newRenderer(".helloName").setData(data).render()); // helloNames List<String> additionalNames = Arrays.asList("Bob", "Cid", "Dee"); data.put("additionalNames", additionalNames); System.out.println("-----------------"); System.out.println( simpleTofu.newRenderer(".helloNames").setData(data).render()); } }
This example exercises the
.helloName
template with a JavaMap
in which the parametername
is mapped to the stringAna
. For the.helloNames
template, the example maps the parameteradditionalNames
to a list of stringsBob
,Cid
,Dee
. -
Run
mvn package
at the root of your project. You should see this message at standard out:Hello world! ----------------- Hello Ana! ----------------- Hello Ana!<br>Hello Bob!<br>Hello Cid!<br>Hello Dee!
If your application uses Guice, you
can inject Soy classes such as SoyFileSet.Builder
instead of constructing them
yourself. Your Guice injector must contain SoyModule
.
For example, if you used Guice, the Hello World example from the previous section would start like this (with three additional import lines not shown):
// Create a Guice injector that contains the SoyModule and use it get a SoyFileSet.Builder.
Injector injector = Guice.createInjector(new SoyModule());
SoyFileSet.Builder sfsBuilder = injector.getInstance(SoyFileSet.Builder.class);
// Bundle the Soy files for your project into a SoyFileSet.
SoyFileSet sfs = sfsBuilder.add(new File("simple.soy")).build();
You might find it error-prone to type hard-coded strings for template names and
template parameters. If so, you can use SoyParseInfoGenerator
to generate Java
classes for the template and parameter names in your templates. Follow the steps
below to use SoyParseInfoGenerator
with the Hello World example:
-
Download the latest version of
SoyParseInfoGenerator.jar
from Maven Central and put it at the project root.Unlike the main Soy jar,
SoyParseInfoGenerator.jar
is executable, and can be run from the command line withjava -jar SoyParseInfoGenerator.jar
. The jar parses Soy files and generates Java classes that contain information such as template and parameter names. (Typically, you would want to run SoyParseInfoGenerator as part of a build step. This can be done with exec-maven-plugin for example, but it is outside the scope of this codelab.)Run
SoyParseInfoGenerator
with the following flags:$ java -jar SoyParseInfoGenerator.jar \ --generateInvocationBuilders --javaPackage example \ --srcs src/main/java/example/simple.soy
This step creates the file
src/main/java/example/SimpleTemplates.java
.Open
src/main/java/example/SimpleTemplates.java
and look at the inner class thatSoyParseInfoGenerator
generated for each of the templates. For example, the classHelloName
represents the.helloName
template andHelloName.Builder#setName
represents the.helloName
template's parametername
. -
Edit
src/main/java/example/HelloWorld.java
to look like this:
package example;
import com.google.template.soy.SoyFileSet;
import com.google.template.soy.tofu.SoyTofu;
import example.SimpleTemplates;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HelloWorld {
public static void main(String[] args) {
SoyFileSet sfs = SoyFileSet
.builder()
.add(HelloWorld.class.getResource("simple.soy"))
.build();
SoyTofu tofu = sfs.compileToTofu();
System.out.println(
tofu.newRenderer(SimpleTemplates.HelloWorld.getDefaultInstance()).render());
SoyTofu simpleTofu = tofu.forNamespace("examples.simple");
System.out.println("-----------------");
System.out.println(
simpleTofu
.newRenderer(SimpleTemplates.HelloName.builder().setName("Ana").build())
.render());
List<String> additionalNames = Arrays.asList("Bob", "Cid", "Dee");
System.out.println("-----------------");
System.out.println(
simpleTofu
.newRenderer(
SimpleTemplates.HelloNames.builder()
.setName("Ana")
.setAdditionalNames(additionalNames)
.build())
.render());
}
}
-
Run
mvn package
at the root of your project. You should see the same message as before:Hello world! ----------------- Hello Ana! ----------------- Hello Ana!<br>Hello Bob!<br>Hello Cid!<br>Hello Dee!
You've just completed the Soy Hello World using Java. Where should you go next?
- To use the same templates from this chapter in JavaScript, try the Hello World Using JavaScript examples.
- To read more about Soy concepts, take a look at the Concepts chapter.