pod afEfanExtra

(deprecated) This project has been renamed to: afEfanXtra

Mixins

EfanConfigIds

Config values as used by Efan.

EfanExtra

(Service) - Service methods for discovering and rendering efan components.

EfanTemplateConverters

(Service) - Contribute to convert files to efan templates.

EfanTemplateDirectories

(Service) - Contribute directories that may contain efan / slim templates.

Classes

EfanExtraModule

The afIoc module class.

Facets

Component

Annotate your efan Components with @Component.

Overview

(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

Overdue.efan:

Dear <%= userName %>,

It appears the following rented DVDs are overdue:

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

Please return them at your convenience.

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

Overdue.fan:

using afIoc
using afEfanExtra

@Component
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() {
    dvdService.findByName(userName)
  }
}

AppModule.fan:

using afIoc

class AppModule {

  static Void bind(ServiceBinder binder) {
    binder.bindImpl(DvdService#)
  }

  @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.

Components

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

Mixins

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.

Templates

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:

/fan/components/Layout.fan
/fan/components/Layout.efan

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 {
  ...
}

Libraries

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) {
    binder.bindImpl(Slim#)
  }

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

Release Notes

v0.0.8

  • 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)

v0.0.6

  • 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.

v0.0.4

  • 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.

v0.0.2

  • New: Preview Release