com.helger.bootstrap3.form.BootstrapFormGroupRendererDefault Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ph-bootstrap3 Show documentation
Show all versions of ph-bootstrap3 Show documentation
Library wrapping Bootstrap 3 controls as Java web application components
/**
* Copyright (C) 2014 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.helger.bootstrap3.form;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import com.helger.bootstrap3.BootstrapHelper;
import com.helger.bootstrap3.CBootstrapCSS;
import com.helger.bootstrap3.grid.BootstrapGridSpec;
import com.helger.commons.annotations.Nonempty;
import com.helger.commons.annotations.OverrideOnDemand;
import com.helger.commons.collections.ContainerHelper;
import com.helger.commons.string.StringHelper;
import com.helger.html.css.DefaultCSSClassProvider;
import com.helger.html.css.ICSSClassProvider;
import com.helger.html.hc.IHCControl;
import com.helger.html.hc.IHCElement;
import com.helger.html.hc.IHCElementWithChildren;
import com.helger.html.hc.IHCNode;
import com.helger.html.hc.html.AbstractHCEdit;
import com.helger.html.hc.html.HCCheckBox;
import com.helger.html.hc.html.HCDiv;
import com.helger.html.hc.html.HCLabel;
import com.helger.html.hc.html.HCRadioButton;
import com.helger.html.hc.htmlext.HCUtils;
import com.helger.validation.error.IError;
import com.helger.validation.error.IErrorList;
import com.helger.webctrls.custom.impl.HCFormLabelUtils;
/**
* This is the default implementation of {@link IBootstrapFormGroupRenderer}
* which performs standard rendering. It offers the possibility to modify
* certain styling by overriding the provided protected methods.
*
* @author Philip Helger
*/
@Immutable
public class BootstrapFormGroupRendererDefault implements IBootstrapFormGroupRenderer
{
public static final ICSSClassProvider CSS_CLASS_FORM_GROUP_HELP_TEXT = DefaultCSSClassProvider.create ("form-group-help-text");
public static final ICSSClassProvider CSS_CLASS_FORM_GROUP_ERROR_TEXT = DefaultCSSClassProvider.create ("form-group-error-text");
/**
* Modify the first control that is inserted. This method is only called when
* a label is present.
*
* @param aLabel
* The label that was provided. Never null
.
* @param aFirstControl
* The first control that was provided. Never null
.
*/
@OverrideOnDemand
protected void modifyFirstControlIfLabelIsPresent (@Nonnull final IHCElementWithChildren > aLabel,
@Nonnull final IHCControl > aFirstControl)
{
// Set the default placeholder (if none is present)
if (aFirstControl instanceof AbstractHCEdit >)
{
final AbstractHCEdit > aEdit = (AbstractHCEdit >) aFirstControl;
// Only check for null, so that empty string overrides this
// default behaviour
if (aEdit.getPlaceholder () == null)
{
// Trim eventually trailing ":" from string
final String sNewPlaceholder = StringHelper.trimEnd (aLabel.getPlainText (), HCFormLabelUtils.LABEL_END);
aEdit.setPlaceholder (sNewPlaceholder);
}
}
}
/**
* Create the help text node
*
* @param aHelpText
* The source help text. Never null
.
* @return Never null
.
*/
@Nonnull
@OverrideOnDemand
protected IHCElement > createHelpTextNode (@Nonnull final IHCNode aHelpText)
{
final BootstrapHelpBlock aHelpBlock = new BootstrapHelpBlock ().addClass (CSS_CLASS_FORM_GROUP_HELP_TEXT);
aHelpBlock.addChild (aHelpText);
return aHelpBlock;
}
/**
* Retrieve an optional CSS class that is provided to the final node. This
* method is only called if a non-null
and non-empty error list
* is present.
*
* @param aErrorList
* The non-null
and non-empty error list.
* @return May be null
to indicate no CSS class.
*/
@Nullable
@OverrideOnDemand
protected ICSSClassProvider getResultNodeCSSClass (@Nonnull @Nonempty final IErrorList aErrorList)
{
if (aErrorList.containsAtLeastOneError ())
return CBootstrapCSS.HAS_ERROR;
if (aErrorList.containsAtLeastOneFailure ())
return CBootstrapCSS.HAS_WARNING;
return null;
}
/**
* Create the node for a single error.
*
* @param aError
* The provided error. Never null
.
* @return Never null
.
*/
@Nonnull
@OverrideOnDemand
protected IHCElement > createSingleErrorNode (@Nonnull final IError aError)
{
final BootstrapHelpBlock aErrorBlock = new BootstrapHelpBlock ().addClass (CSS_CLASS_FORM_GROUP_ERROR_TEXT);
aErrorBlock.addChild (aError.getErrorText ());
return aErrorBlock;
}
/**
* Callback possibility to change the finally created node before it is
* returned. By default nothing happens in here.
*
* @param aForm
* The source form. Never null
.
* @param aFormGroup
* The source form group. Never null
.
* @param aFinalNode
* The created node so far. Never null
.
*/
@OverrideOnDemand
protected void modifyFinalNode (@Nonnull final BootstrapForm aForm,
@Nonnull final BootstrapFormGroup aFormGroup,
@Nonnull final HCDiv aFinalNode)
{}
@Nonnull
public IHCElement > renderFormGroup (@Nonnull final BootstrapForm aForm,
@Nonnull final BootstrapFormGroup aFormGroup)
{
final EBootstrapFormType eFormType = aForm.getFormType ();
final BootstrapGridSpec aLeftGrid = aForm.getLeft ();
final BootstrapGridSpec aRightGrid = aForm.getRight ();
final IHCElementWithChildren > aLabel = aFormGroup.getLabel ();
final IHCNode aCtrls = aFormGroup.getCtrl ();
final IHCNode aHelpText = aFormGroup.getHelpText ();
final IErrorList aErrorList = aFormGroup.getErrorList ();
final List > aAllCtrls = HCUtils.getAllHCControls (aCtrls);
// Set CSS class to all contained controls
BootstrapHelper.markAsFormControls (aAllCtrls);
final IHCControl > aFirstControl = ContainerHelper.getFirstElement (aAllCtrls);
HCDiv aFinalNode;
final boolean bFirstControlIsCheckBox = aFirstControl instanceof HCCheckBox;
final boolean bFirstControlIsRadioButton = aFirstControl instanceof HCRadioButton;
if (bFirstControlIsCheckBox || bFirstControlIsRadioButton)
{
// Check box or radio button
final HCDiv aCtrlDiv = new HCDiv ();
if (bFirstControlIsCheckBox)
aCtrlDiv.addClass (CBootstrapCSS.CHECKBOX);
else
if (bFirstControlIsRadioButton)
aCtrlDiv.addClass (CBootstrapCSS.RADIO);
if (aLabel == null || !aLabel.hasChildren ())
{
aCtrlDiv.addChild (new HCLabel ().addChild (aCtrls));
}
else
{
aLabel.addChild (0, aCtrls);
aLabel.addChild (1, " ");
aCtrlDiv.addChild (aLabel);
}
if (eFormType == EBootstrapFormType.HORIZONTAL)
{
final HCDiv aCtrlParent = new HCDiv ();
aLeftGrid.applyOffsetTo (aCtrlParent);
aRightGrid.applyTo (aCtrlParent);
aFinalNode = new HCDiv ().addClass (CBootstrapCSS.FORM_GROUP).addChild (aCtrlParent.addChild (aCtrlDiv));
}
else
aFinalNode = aCtrlDiv;
}
else
{
// Other control - add in form group
aFinalNode = new HCDiv ().addClass (CBootstrapCSS.FORM_GROUP);
if (aLabel != null && aLabel.hasChildren ())
{
// We have a label
// Screen reader only....
if (eFormType == EBootstrapFormType.INLINE)
aLabel.addClass (CBootstrapCSS.SR_ONLY);
else
if (eFormType == EBootstrapFormType.HORIZONTAL)
{
aLabel.addClass (CBootstrapCSS.CONTROL_LABEL);
aLeftGrid.applyTo (aLabel);
}
if (aFirstControl != null)
{
// We have a label for a control
if (aLabel instanceof HCLabel)
((HCLabel) aLabel).setFor (aFirstControl);
modifyFirstControlIfLabelIsPresent (aLabel, aFirstControl);
}
if (eFormType == EBootstrapFormType.HORIZONTAL)
{
final HCDiv aCtrlParent = new HCDiv ();
aCtrlParent.addChild (aCtrls);
aRightGrid.applyTo (aCtrlParent);
aFinalNode.addChildren (aLabel, aCtrlParent);
}
else
aFinalNode.addChildren (aLabel, aCtrls);
}
else
{
// No label - just add controls
if (eFormType == EBootstrapFormType.HORIZONTAL)
{
final HCDiv aCtrlParent = new HCDiv ();
aLeftGrid.applyOffsetTo (aCtrlParent);
aRightGrid.applyTo (aCtrlParent);
aFinalNode.addChild (aCtrlParent.addChild (aCtrls));
}
else
aFinalNode.addChild (aCtrls);
}
}
// Help text
if (aHelpText != null)
{
final IHCElement > aHelpTextNode = createHelpTextNode (aHelpText);
if (eFormType == EBootstrapFormType.INLINE)
aHelpTextNode.addClass (CBootstrapCSS.SR_ONLY);
if (eFormType == EBootstrapFormType.HORIZONTAL)
((HCDiv) aFinalNode.getLastChild ()).addChild (aHelpTextNode);
else
aFinalNode.addChild (aHelpTextNode);
}
// Check form errors - highlighting
if (aErrorList != null && !aErrorList.isEmpty ())
{
aFinalNode.addClass (getResultNodeCSSClass (aErrorList));
for (final IError aError : aErrorList)
{
final IHCElement > aErrorNode = createSingleErrorNode (aError);
if (eFormType == EBootstrapFormType.HORIZONTAL)
{
aLeftGrid.applyOffsetTo (aErrorNode);
aRightGrid.applyTo (aErrorNode);
}
aFinalNode.addChild (aErrorNode);
}
}
modifyFinalNode (aForm, aFormGroup, aFinalNode);
return aFinalNode;
}
}