efanExtraUser Guide


(deprecated) This project has been renamed to: afEfanXtra. Please use this newer version instead.

afEfanExtra is a library for creating managed libraries of reusable Embedded Fantom (efan) components. Influenced by Java's Tapestry 5, it pairs up Fantom classes and efan templates to encapsulate model / view behaviour.

afEfanExtra extends afEfan, is powered by afIoc and works great with afSlim.

afEfanExtra works great in a web / afBedSheet environment, with URLs being automatically mapped to components (see afBedSheetEfanExtra), but is presented here context free.

Quick Start


Dear <%= userName %>,

It appears the following rented DVDs are overdue:

    <%= dvds.join(", ") %>

Please return them at your convenience.

<% app.renderSignOff("The Management") %>


using afIoc
using afEfanExtra

const mixin Overdue {

  // use afIoc services!
  @Inject abstract DvdService? dvdService

  // template can access mixin fields
  abstract Str? userName

  // called before the component is rendered
  Void initialise(Str userName) {
    this.userName = userName

  // methods may be called from the template
  Str[] dvds() {


using afIoc

class AppModule {

  static Void bind(ServiceBinder binder) {

  @Contribute { serviceType=EfanLibraries# }
  static Void contributeEfanLibraries(MappedConfig config) {

    // contribute all components in the pod as a library named 'app'
    config["app"] = AppModule#.pod

Then to render a component:

efanExtra.render(Overdue#, "Mr Smith")

Full example source code available on BitBucket.


An efan component consists of a Fantom const mixin class and a corresponding efan template file.


Component mixins must be annotated with the @Component facet.

All fields and methods of the mixin are directly accessible in the template. You can even use afIoc's @Inject facet to inject services just as you would in a service class.

Use an initialise() method to pass state into an efan component (the efan ctx variable is not used). Only one initialise() method is allowed. It must be named initialise but may take any number of parameters.


By default, the template file has the same name as the component (with a .efan extension) and lives anywhere in a pod resource dir. Example component files:


Ensure /fan/components/ is listed in your build.fan as a resDir.

ALIEN-AID: Note resource directories in your build.fan are NOT nested. Adding res/ will NOT add /res/components/ - /res/components/ would need to be added seperately. Example:

resDirs = [`doc/`, `res/`, `res/components/`]

If you wish the template to have a different name to the Fantom class, then you can set an explicit Uri with the template field on @Component. Example:

@Component { template=`fan://acmePod/templates/Notice.efan` }
const mixin Overdue {


Components are managed in libraries. To package up your components, add to the following to your app module:

@Contribute { serviceType=EfanLibraries# }
static Void contributeEfanLibraries(MappedConfig config) {

  // contribute all components in the pod as a library named 'app'
  config["app"] = AppModule#.pod

Library classes are automatically added as fields in your components. Library classes contain component render methods. In the example above, the library (in a field named app) has 2 render methods, available to your templates:

app.renderOverdue(Str userName)
app.renderSignOff(Str who)

ALIEN-AID: Library render methods are logged at registry startup so you don't have to remember them!

Use with Slim

afEfanExtra works great with afSlim! Add the following to your AppModule and afEfanExtra will automatically pick up component templates with the extenstion .slim:

using afIoc
using afSlim
using afEfanExtra

class AppModule {

  static Void bind(ServiceBinder binder) {

  @Contribute { serviceType=EfanTemplateConverters# }
  internal static Void contributeSlimTemplates(MappedConfig config, Slim slim) {
    config["slim"] = |File file -> Str| {

Release Notes


  • New: Added EfanTemplateDirectories service which scans external directories for efan templates.
  • New: Compilation err msgs are updated with code hints should a simple component rendering typo be spotted. (ALIEN-AID)


  • New: Fields may be annotated with any facet, not just @Inject. Think @Config!!!
  • Chg: Updated to efan-1.3.2
  • Chg: Better Err msgs if component template not found.


  • New: Component templates can be specified via the @Component.template field.
  • New: Added a contributable EfanTemplateFinders service.
  • New: Ability to supress startup log messages.
  • New: Added compiler hooks (mainly for afBedSheetEfanExtra).
  • New: EfanExtra.component() returns component instances.
  • Chg: @Component facet is inherited.


  • New: Preview Release