I have a vraptor4 project and i want to use apache velocity
as the template engine.
So i specialized the br.com.caelum.vraptor.view.DefaultPathResolver
as
@Specializes
public class VelocityPathResolver extends DefaultPathResolver {
@Inject
protected VelocityPathResolver(FormatResolver resolver) {
super(resolver);
}
protected String getPrefix() {
return "/WEB-INF/vm/";
}
protected String getExtension() {
return "vm";
}
}
That work fine, but I cannot have @Named
components in my templates.
Having
@SessionScoped
@Named("mycmp")
public class MyComponent implements Serializable {
private static final long serialVersionUID = 1L;
private String name = "My Component";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I cannot refer to it as ${mycmp.name}
in my velocity template (.vm), but if i use .jsp it works fine.
To solve it i specialized the br.com.caelum.vraptor.core.DefaultResult
as
@Specializes
public class VelocityResult extends DefaultResult {
private final MyComponent mycmp;
@Inject
public VelocityResult(HttpServletRequest request, Container container, ExceptionMapper exceptions, TypeNameExtractor extractor,
Messages messages, MyComponent mycmp) {
super(request, container, exceptions, extractor, messages);
this.mycmp = mycmp;
}
@PostConstruct
public void init() {
include("mycmp", mycmp);
}
}
Is there a better approach for having @Named
components in velocity templates?
it looks like that CDI's @Named
won't work with velocity templates, but you can implement an Interceptor
to do this job for you. An example would be:
@Intercepts
public class IncluderInterceptor {
@Inject private MyComponent mycmp;
@AfterCall public void after() {
result.include("mycmp", mycmp);
// any other bean you want to
}
}
And thinking in a more flexible solution, you could create an annotation and use it to define which bean should be included... something like that:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Included {
}
So you could add @Included
at your classes:
@Included public class MyComponent { ... }
And just add the accept method on the IncluderInterceptor
:
@Intercepts
public class IncluderInterceptor {
@Inject @Any Instance<Object> allBeans;
@AfterCall public void after() {
// foreach allBeans, if has @Included, so
// include bean.getClass().getSimpleName()
// with first letter lower case, or something
}
}
Sure, if you'll include just a few beans the first solution should be enought. Best regards