freemarker如何读取js变量

JavaScript019

freemarker如何读取js变量,第1张

首先是我们自定义的工具类FreeMarkerUtil.java

package com.jadyer.util

import java.io.BufferedWriter

import java.io.File

import java.io.FileOutputStream

import java.io.IOException

import java.io.OutputStreamWriter

import java.io.PrintWriter

import java.io.Writer

import java.util.Map

import freemarker.template.Configuration

import freemarker.template.Template

import freemarker.template.TemplateException

public class FreeMarkerUtil {

    /**

     * 获取指定目录下的模板文件

     * @param name       模板文件的名称

     * @param pathPrefix 模板文件的目录

     */

    public Template getTemplate(String name, String pathPrefix) throws IOException{

        Configuration cfg = new Configuration() //通过FreeMarker的Configuration对象可以读取ftl文件

        cfg.setClassForTemplateLoading(this.getClass(), pathPrefix) //设置模板文件的目录

        cfg.setDefaultEncoding("UTF-8")       //Set the default charset of the template files

        Template temp = cfg.getTemplate(name) //在模板文件目录中寻找名为"name"的模板文件

        return temp //此时FreeMarker就会到类路径下的"pathPrefix"文件夹中寻找名为"name"的模板文件

    }

    

    /**

     * 根据模板文件输出内容到控制台

     * @param name       模板文件的名称

     * @param pathPrefix 模板文件的目录

     * @param rootMap    模板的数据模型

     */

    public void print(String name, String pathPrefix, Map<String,Object> rootMap) throws TemplateException, IOException{

        this.getTemplate(name, pathPrefix).process(rootMap, new PrintWriter(System.out))

    }

    

    /**

     * 根据模板文件输出内容到指定的文件中

     * @param name       模板文件的名称

     * @param pathPrefix 模板文件的目录

     * @param rootMap    模板的数据模型

     * @param file       内容的输出文件

     */

    public void printFile(String name, String pathPrefix, Map<String,Object> rootMap, File file) throws TemplateException, IOException{

        Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))

        this.getTemplate(name, pathPrefix).process(rootMap, out) //将模板文件内容以UTF-8编码输出到相应的流中

        if(null != out){

            out.close()

        }

    }

}

2.下面是位于//src//ftl//包中用于演示自定义指令的用法的macro.ftl

<#-- 自定义指令 -->

<#-- 自定义一个名字为myMacro的指令 -->

<#-- 该指令的作用就是输出<h2>Hi:macro</h2>,调用该指令时使用<@myMacro/>即可 -->

<#macro myMacro>

    <h1>Hi:macro</h1>

</#macro>

<@myMacro/>

<#-- 自定义一个带有参数的名字为myMacro22的指令 -->

<#-- 这里myMacro22后面跟的都是参数,这里有两个参数,分别为num和name -->

<#macro myMacro22 num name>

    <#list 1..num as n>

        <h2>hello:${name}${n}${n}</h2>

    </#list>

</#macro>

<@myMacro22 4 "blackjade"/>

<@myMacro22 num=4 name="Jadyer"/>

<#-- 为指令参数定义初始值 -->

<#-- 此时就可以直接调用<@myMacro33/>而不会报错了。注意:指定初始值的参数要放在未指定初始值参数的后面 -->

<#macro myMacro33 num=3 name="JADYER">

    <#list 1..num as n>

        <h3>welcome:${name}${n}${n}</h3>

    </#list>

</#macro>

<@myMacro33/>

<@myMacro33 num=4 name="myblog"/>

<#-- 使用<#nested/>可以输出指令中的内容,输出的次数与<#nested/>的数量有关 -->

<#macro myMacro44>

    <#nested/>

    <#nested/>

</#macro>

<@myMacro44>

    <h4>http://blog.csdn.net/jadyer</h4>

</@myMacro44>

<#-- 实际输出时会根据<#nested/>的数量来打印,而且<#nested/>还可以传参给<@myMacro55> -->

<#macro myMacro55>

    <#nested 55 66/>

    <#nested 55 77/>

    <#nested 55 88/>

</#macro>

<@myMacro55x,y>

    <h5>It is ${x}and${y}</h5>

</@myMacro55>

3.下面是位于//src//ftl//包中用于演示四种变量的用法的assign.ftl

<#--

变量,分为以下四类

模型变量----rootMap中的变量

模板变量----使用<#assign>定义的变量

局部变量----指令中的变量

循环变量----循环中的变量

-->

<#assign myname="Jadyer"/>

<#-- 此时模板变量名称与模型变量名称相同,虽然打印的是模板变量值,但模型变量值并没有被覆盖,而被隐藏了 -->

<#-- 并且这两个变量是存在于不同空间中的,如此才给人一种"被隐藏"的意识,这是FreeMarker中很重要的一个概念 -->

<#-- 在调用变量的时候,实际上它会先到模板变量中查找,没有则去模型变量中查找 -->

${myname}

<#-- 通过这种方式就可以访问模型变量了 -->

${.globals.myname}

<#-- 调用该指令后,会将模板变量myname的值覆盖为JADYER。注意此时是覆盖,不是隐藏,相当于模板的覆盖模板的 -->

<#macro myMacro>

    <#assign myname="JADYER"/>

</#macro>

<#-- 由于此时还没有调用myMacro指令,故打印的仍是最上面定义的模板变量值Jadyer -->

${myname}

<@myMacro/>

<#-- 由于此时已经调用了myMacro指令,故打印的是模板中的变量被覆盖后的值JADYER -->

${myname}

<#-- 上面那种在指令中定义变量的方式存在风险,容易误将模板变量中同名的变量覆盖 -->

<#-- 而使用<#local/>定义的变量则不会覆盖模板变量,且此时的变量就属于局部变量了 -->

<#macro myMacro22>

    <#local myname="http://blog.csdn.net/jadyer"/>

    ${myname}

</#macro>

${myname}

<@myMacro22/>

${myname}

<#-- 循环变量的作用域为循环体内 -->

<#list 1..3 as myname>

    ${myname}

</#list>

${myname}

4.最后是使用JUnit4.x编写的测试类FreeMarkerTest.java

package com.jadyer.test

import java.io.IOException

import java.util.HashMap

import java.util.Map

import org.junit.Before

import org.junit.Test

import com.jadyer.util.FreeMarkerUtil

import freemarker.template.TemplateException

public class FreeMarkerTest {

    String pathPrefix

    Map<String,Object> rootMap

    FreeMarkerUtil fmu

    

    @Before

    public void setUp(){

        pathPrefix = "/ftl"

        rootMap = new HashMap<String,Object>()

        fmu = new FreeMarkerUtil()

    }

    

    @Test

    public void printMacro() throws TemplateException, IOException{

        fmu.print("macro.ftl", pathPrefix, rootMap)

    }

    

    @Test

    public void printAssign() throws TemplateException, IOException{

        rootMap.put("myname", "XuanYu") //这个变量就是数据模型中的变量,即模型变量

        fmu.print("assign.ftl", pathPrefix, rootMap)

    }

}

大段的 HTML 嵌入到 JS 里结果就是悲剧。不能代码高亮不能自动缩进,太难维护了。

我的经验是,直接把 HTML 单独写到一个浏览器能访问到的文件里,比如 template/foo.html。然后 JS 里发一个同步 XHR 请求去读这个文件,例如:

var html = Template.load('/template/foo.html')

var target = document.getElementById('xxx')

target.innerHTML = html

Template 是一个工具类,负责发送同步 XHR 请求并返回结果。

这样在开发的时候,模板文件和 JS 代码分离,非常好维护。

当然这样做的话,上线的时候总发 XHR 请求也不是办法。所以在打包 JS 之前,我一般会通过脚本把所有的 Template.load('.*') 提取出来,替换成对应 HTML 的内容。这样在开发时非常方便,上线时也没有性能问题。

比如上面的代码被打包工具跑一下就变成了:

var html = "\n...\n"// 引号里是 /template/foo.html 对引号、换行做了转义之后的内容,由工具自动生成

var target = document.getElementById('xxx')

target.innerHTML = html