Spring MVC对FreeMarker提供了支持,包括页面宏的封装(spring.ftl)。但是官方默认的页面宏封装得相当不友好,使用起来很不方便。如果在Spring MVC中用FreeMarker作为页面模版技术,建议还是重写对FreeMarker的页面宏的封装。以下是一个重新封装过的FreeMarker宏文件,主要包含文本信息、链接、表单元素的宏,常规的应用已可满足,源文件可以参考这里 mvc.ftl 。
<#macro message code args>
<@compress>
<#if args?? && args?size != 0>
${springMacroRequestContext.getMessage(code, args)}
<#else>
${springMacroRequestContext.getMessage(code)}
</#if>
</@compress>
</#macro>
<#macro url url params...>
<@compress>
<#if params?? && params?size!=0>
${springMacroRequestContext.getContextUrl(url, params)}
<#else>
${springMacroRequestContext.getContextUrl(url)}
</#if>
</@compress>
</#macro>
<#macro a href attributes...>
<@compress>
<a href="<@url href />" ${getAttributes(attributes)}><#nested></a>
</@compress>
</#macro>
<#macro bind path>
<#if htmlEscape?exists>
<#assign status = springMacroRequestContext.getBindStatus(path, htmlEscape)>
<#else>
<#assign status = springMacroRequestContext.getBindStatus(path)>
</#if>
<#if status.value?exists && status.value?is_boolean>
<#assign stringStatusValue = status.value?string>
<#else>
<#assign stringStatusValue = status.value?default("")>
</#if>
<#assign id = status.expression?replace("[", "")?replace("]", "")>
<#assign name = status.expression>
</#macro>
<#macro form action method="post" attributes...>
<form method="${method}" action="<@url "${action}" />" ${getAttributes(attributes)}>
<#nested>
</form>
</#macro>
<#macro input path attributes...>
<@bind path />
<@replaceAttributes attributes />
<@compress>
<input type="text" id="${id}" name="${name}" value="${stringStatusValue}" ${getAttributes(attributes)} />
</@compress>
</#macro>
<#macro password path attributes...>
<@bind path />
<@replaceAttributes attributes />
<@compress>
<input type="password" id="${id}" name="${name}" ${getAttributes(attributes)} />
</@compress>
</#macro>
<#macro hidden path attributes...>
<@bind path />
<@replaceAttributes attributes />
<input type="hidden" id="${id}" name="${name}" value="${stringStatusValue}" ${getAttributes(attributes)} />
</#macro>
<#macro textarea path attributes...>
<@bind path />
<@replaceAttributes attributes />
<@compress>
<textarea id="${id}" name="${name}" ${getAttributes(attributes)}>${stringStatusValue}</textarea>
</@compress>
</#macro>
<#macro select path items itemLabel itemValue headerLabel headerValue="" attributes...>
<@bind path />
<@replaceAttributes attributes />
<select id="${id}" name="${name}" ${getAttributes(attributes)}>
<#if headerLabel??>
<option value="${headerValue}">${headerLabel}</option>
</#if>
<@options items itemValue itemLabel status.value />
</select>
</#macro>
<#macro boolSelect path headerLabel headerValue="" trueLabel="是" trueValue="1" falseLabel="否" falseValue="0" attributes...>
<@bind path />
<@replaceAttributes attributes />
<select id="${id}" name="${name}" ${getAttributes(attributes)}>
<#if headerLabel??>
<option value="${headerValue}">${headerLabel}</option>
</#if>
<#local isTrueSelected = status.value?? && status.value?string=="true">
<#local isFalseSelected = status.value?? && status.value?string=="false">
<option value="${trueValue}" <#if isTrueSelected> selected="selected"</#if>>${trueLabel}</option>
<option value="${falseValue}" <#if isFalseSelected> selected="selected"</#if>>${falseLabel}</option>
</select>
</#macro>
<#macro options items itemValue itemLabel values>
<@bindOptions items itemValue itemLabel values />
<#list opts?keys as optKey>
<#local optVal = opts[optKey]>
<#local isSelected = contains(vals, optKey)>
<option value="${optKey?html}"<#if isSelected> selected="selected"</#if>>${optVal?html}</option>
</#list>
</#macro>
<#macro radios path items itemValue itemLabel prefix suffix attributes...>
<@bind path />
<@bindOptions items itemValue itemLabel status.value />
<#list opts?keys as optKey>
<@replaceAttributes attributes />
<#local optVal = opts[optKey]>
<#local isChecked = contains(vals, optKey)>
<@compress single_line=true>
${prefix}
<input type="radio" id="${id}" name="${name}" value="${optKey?html}"<#if isChecked> checked="checked"</#if> ${getAttributes(attributes)} />
${optVal?html}
${suffix}
</@compress>
</#list>
</#macro>
<#macro radio path prefix suffix trueText="是" falseText="否" attributes...>
<#if path??>
<@bind path />
<@replaceAttributes attributes />
<#local isChecked = status.value?? && status.value?string=="true">
${prefix}<input type="radio" id="${id}" name="${name}" value="1"<#if isChecked> checked="checked"</#if> ${getAttributes(attributes)} />${trueText}${suffix}
${prefix}<input type="radio" id="${id}" name="${name}" value="0"<#if !isChecked> checked="checked"</#if> ${getAttributes(attributes)} />${falseText}${suffix}
<#else>
${prefix}<input type="radio" ${getAttributes(attributes)} />${suffix}
</#if>
</#macro>
<#macro checkboxs path items itemValue itemLabel prefix suffix attributes...>
<@bind path />
<#local values = status.value?split(",")>
<@bindOptions items itemValue itemLabel values />
<#list opts?keys as optKey>
<@replaceAttributes attributes />
<#local optVal = opts[optKey]>
<#local isChecked = contains(vals, optKey)>
<@compress single_line=true>
${prefix}
<input type="checkbox" id="${id}" name="${name}" value="${optKey?html}"<#if isChecked> checked="checked"</#if> ${getAttributes(attributes)} />
${optVal?html}
${suffix}
</@compress>
</#list>
<input type="hidden" name="_${status.expression}" value="on"/>
</#macro>
<#macro checkbox path attributes...>
<#if path??>
<@bind path />
<@replaceAttributes attributes />
<#local isChecked = status.value?? && status.value?string=="true">
<input type="hidden" name="_${status.expression}" value="on"/>
<input type="checkbox" id="${id}" name="${name}"<#if isChecked> checked="checked"</#if> ${getAttributes(attributes)} />
<#else>
<input type="checkbox" ${getAttributes(attributes)} />
</#if>
</#macro>
<#macro errors path separator attributes...>
<#if path??>
<@bind path />
</#if>
<#list status.errorMessages as error>
<span ${getAttributes(attributes)}>${error}</span>
<#if error_has_next>${separator}</#if>
</#list>
</#macro>
<#macro replaceAttributes attributes>
<#if attributes?? && attributes?size gt 0>
<#if contains(attributes?keys, "id")>
<#assign id = attributes["id"]>
</#if>
<#if contains(attributes?keys, "name")>
<#assign name = attributes["name"]>
</#if>
<#if contains(attributes?keys, "value")>
<#assign stringStatusValue = attributes["value"]>
</#if>
</#if>
</#macro>
<#macro bindOptions items itemValue itemLabel values>
<#if itemValue?? && itemLabel??>
<#assign opts = getOptions(items, itemValue, itemLabel)>
<#else>
<#assign opts = items>
</#if>
<#if values??>
<#if !values?is_enumerable>
<#assign vals = [values]>
<#else>
<#assign vals = values>
</#if>
<#else>
<#assign vals = []>
</#if>
</#macro>
<#function contains items target>
<#list items as item>
<#if item == target><#return true></#if>
</#list>
<#return false>
</#function>
<#function getAttributes attributes>
<#local attrs>
<@compress single_line=true>
<#if attributes?? && attributes?size gt 0>
<#list attributes?keys as attributeName>
${attributeName}="${attributes[attributeName]}"
</#list>
</#if>
</@compress>
</#local>
<#return attrs>
</#function>
<#function getOptions items itemValue itemLabel>
<#local opts>
<@compress single_line=true>
{
<#if items?? && items?size gt 0>
<#list items as item>
"${("item." + itemValue)?eval}":"${("item." + itemLabel)?eval}"<#if item_has_next>,</#if>
</#list>
</#if>
}
</@compress>
</#local>
<#return opts?eval>
</#function>