closure-builder
Version:
Simple Closure, Soy and JavaScript Build system
349 lines (285 loc) • 13.8 kB
Markdown
# Hello World Using Java
## Hello World
Follow these steps to create a simple Hello World template and use it in Java:
1. Create a new [Maven](https://maven.apache.org) project using your favorite
IDE. (`Closure Templates` 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
└── resources
└── example
└── simple.soy
```
2. All files that contain `Closure Templates` end with the `.soy` file extension
and are called Soy files. Create `src/main/resources/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.
3. Copy the following template to `src/main/resources/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 name `examples.simple.helloWorld`.
4. 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 `Closure Templates`.
`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](https://maven.apache.org/guides/introduction/introduction-to-the-pom.html)
for more information about the structure of `pom.xml`.)
5. 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 a `SoyFileSet` object, then compiles the bundle into a
`SoyTofu` object with a call to `compileToTofu()`. The final line calls the
template, using the template's fully qualified name
`examples.simple.helloWorld`, and renders its output to standard out.
6. 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>
7. Run `mvn package` at the root of your project. You should see this message
at standard out:
Hello world!
## Hello Name and Hello Names
1. Add the following second template, called `.helloName`, to `simple.soy`.
Note that `.helloName` takes a required parameter called `name`, which is
declared by `@param`. It also takes an optional parameter `greetingWord`,
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 the `if-else` commands. You can put this
template before or after the `.helloWorld` template, just as long as it's
after the `namespace` 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}
2. Add a third template to the file. This template, `helloNames`, demonstrates
a `for` loop with an `ifempty` command. It also shows how to call other
templates and insert their output using the `call` command. Note that the
`data="all"` attribute in the `call` 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}
3. 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 Java `Map` in which
the parameter `name` is mapped to the string `Ana`. For the `.helloNames`
template, the example maps the parameter `additionalNames` to a list of
strings `Bob`, `Cid`, `Dee`.
4. 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!
## Using Guice
If your application uses [Guice](http://code.google.com/p/google-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):
~~~ {.prettyprint}
// 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();
~~~
## Using SoyParseInfoGenerator
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
constants for the template and parameter names in your templates. Follow the
steps below to use `SoyParseInfoGenerator` with the Hello World example:
1. Download the latest version of `SoyParseInfoGenerator.jar` from [Maven
Central](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22soy%22) and put
it at the project root.
Unlike the main `Closure Templates` jar, `SoyParseInfoGenerator.jar` is
executable, and can be run from the command line with `java -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 \
--outputDirectory src/main/java/example \
--javaPackage example \
--javaClassNameSource filename \
--srcs src/main/resources/example/simple.soy
This step creates the file `src/main/java/example/SimpleSoyInfo.java`. This
file contains mappings between the generated constants and their
corresponding strings.
Open `src/main/java/example/SimpleSoyInfo.java` and look at the constants
that `SoyParseInfoGenerator` generated for each of the templates and their
parameters. For example, the Java constant `HELLO_NAME` maps to a
`SoyTemplateInfo` object that represents the `.helloName` template, and the
constant `HELLO_NAME.NAME` maps to the `.helloName` template's parameter
`name`.
2. Edit `src.main/java/example/HelloWorld.java` to looks like this:
~~~ {.prettyprint}
package example;
import static example.SimpleSoyInfo.HELLO_NAME;
import static example.SimpleSoyInfo.HELLO_NAMES;
import static example.SimpleSoyInfo.HELLO_WORLD;
import com.google.template.soy.SoyFileSet;
import com.google.template.soy.tofu.SoyTofu;
import example.SimpleSoyInfo.HelloNameSoyTemplateInfo;
import example.SimpleSoyInfo.HelloNamesSoyTemplateInfo;
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(SimpleSoyInfo.HELLO_WORLD).render());
SoyTofu simpleTofu = tofu.forNamespace("examples.simple");
Map<String, Object> data = new HashMap<>();
data.put(HelloNameSoyTemplateInfo.NAME, "Ana");
System.out.println(
simpleTofu
.newRenderer(SimpleSoyInfo.HELLO_NAME)
.setData(data)
.render());
List<String> additionalNames = Arrays.asList("Bob", "Cid", "Dee");
data.put(HelloNamesSoyTemplateInfo.ADDITIONAL_NAMES, additionalNames);
System.out.println(
simpleTofu
.newRenderer(SimpleSoyInfo.HELLO_NAMES)
.setData(data)
.render());
}
}
~~~
3. 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 `Closure Templates` 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](helloworld_js.md) examples.
- To read more about `Closure Templates` concepts, take a look at the
[Concepts](../concepts/index.md) chapter.