Kennen Sie dies nicht? Sie haben eine Applikation mit diversen Dialogen, in denen jeweils sowohl Pflicht- als auch optionale Felder enthalten sind. Damit diese beiden Typen für den Anwender unterscheidbar sind, sollen die Pflichfelder markiert werden. Und dass ganze an einer zentralen Stelle, z.B. als Teil eines Templates. Die dahinter liegende Idee ist recht einfach: Vor der Auslieferung an den Browser werden alle Label geprüft, ob die zugehörigen Eingabefelder Pflicht sind. Falls ja, erfolgt die Markierung.

<f:metadata>
     <f:phaseListener type="calculator.LifeCycleListener"/>
 </f:metadata>

Der vorstehende Code registriert ín einer JSF-Seite einen Phase-Listener. In diesem erfolgt die Prüfung und Änderung des Labels

package calculator;

import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.component.UIViewRoot;
import javax.faces.component.html.HtmlOutputLabel;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

/**
 *
 * @author mmueller
 */
public class LifeCycleListener implements PhaseListener {
  UIViewRoot _root;

  @Override
  public void beforePhase(PhaseEvent event) {
    _root = event.getFacesContext().getViewRoot();
    if (_root != null && event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
      markLabels4Required(_root);
    }
  }

  @Override
  public void afterPhase(PhaseEvent event) {
  }

  @Override
  public PhaseId getPhaseId() {
    return PhaseId.ANY_PHASE;
  }

  private void markLabels4Required(UIComponent parent) {
    String marker = " *";
    for (UIComponent child : parent.getChildren()) {
      if (child instanceof HtmlOutputLabel) {
        HtmlOutputLabel label = (HtmlOutputLabel) child;
        String targetId = label.getNamingContainer().getClientId() +
                          ":" + label.getFor();
        UIComponent target = _root.findComponent(targetId);
        if (target instanceof UIInput) {
          UIInput input = (UIInput) target;
          String labelText = label.getValue().toString();
          if (input.isRequired() && !labelText.endsWith(marker)) {
            label.setValue(labelText + marker);
          }
          if (!input.isRequired() && labelText.endsWith(marker)) {
          label.setValue(labelText.substring(0,
                         labelText.length() - marker.length()));
          }
        }
      }
      markLabels4Required(child);
    }
  }
}

Der PhaseListener kann alle Phasen auswerten. Hier geht es um den Beginn von RenderResponse. Hierfür ist auch ein alternativer Ansatz möglich:

<f:metadata>
 <f:event type="preRenderView" listener="#{phaseBean.preRenderView}"/>
</f:metadata>

PhaseBean (Ausschnitt):

[...]
UIViewRoot _root;

public void preRenderView(ComponentSystemEvent e) {
  _root = FacesContext.getCurrentInstance().getViewRoot();
  markLabels4Required(_root);
}
[...]

Dieser Artikel soll Ihnen nur als schnelle Information dienen. Später werde ich dies wahrscheinlich in mein Tutorial aufnehmen und mit zusätzlichen Erläuterungen versehen.