使用过前端模板的同学们,尤其是使用过nodejs写后台服务的同学们,应该对ejs模板和jade模板都不陌生。对与ejs模板和jade模板孰强孰弱,载各大论坛中一直争论不休,有说ejs更直观的,也有说jade更优雅、更强大的。我今天不讨论谁好谁坏,而是记录一下这几天发现的一个特殊的使用场景——递归树形结构渲染。

什么是递归树形结构渲染?

  递归树形结构其实是特指那些父子结构中子级展开后和父级结构相同或类似,并有可能继续展开不断延伸,有点像树形结构中的枝干,每一级的枝干即是上一级枝干的子级,又是下一级枝干的父级,单独从局部上看又是一样的结构,整体又构成了一个层次分明的树。这种结构其实很常见,比如树形文件夹-文件管理、家里的族谱、公司的部门划分、网站分类的菜单等。但是为什么又觉得在开发中感到很陌生,是因为我们平时很少写递归的代码,前端的产品形态也很少使用递归和树形结构,如果有就会去掉用别人的三方js库,如果是app或者桌面应用就不免要遍历文件夹来展示,还有就是前端的交互特点更多倾向于多级联动,即点开某一级的时候再去拉数据加载或计算当前的下一级,比如常见的省份组件和日期选择器。

我遇到的问题——ejs没有递归语法?

  中所周知ejs模板更像是html的扩展,在html的基础上加入了变量和逻辑(条件和循环)以及片段引用。因此常规的情况下是很适合使用,可读性强,前后端的同学都能轻松上手。但是要实现递归的树形结构就要满足一个条件——自己调用自己。ejs并不支持定义一个函数。但是jade就不同,jade有mixin的语法,这从某种程度上就满足了要求。

  但是我现有的项目都是ejs模板实现的,局部使用jade又会使项目的模板引擎管理显得很乱。加上另一个原因就是我的项目是前后端复用同一套模板,而且这个需要递归的功能是在前端模板来实现的,webpack中使用ejs-loader会有另一个问题就是不支持include语法。面前有一个方法就是在前端再加一个jade-loader来递归处理树形的数据。

ejs实现递归树形结构渲染的猜想

  经过一番斗争我找到了在ejs中实现递归树形结构渲染的方法。要记住两个限制:1、尽量不使用include语法,来保证前端工程中ejs-loader的兼容性;2、ejs模板本身不支持函数定义。

  但是这两个不就是递归实现的基本途径吗?是也不是,ejs只是不允许函数的定义,但是却允许函数的调用,即 <%= data %>  中data可以是变量值或表达式,既然是表达式,就说明data可以是某个函数的调用表达式即 <%= data.fn(option) %> 。甚至来说函数的返回值可以是一个html或者另一个模板。

那么函数的定义在哪里完成?

  既然是data.fn 那么fn就是data的一部分,即函数是数据中的一个字段,我们知道webpack 使用ejs-loader时require进来就是个函数。

 var $$tmpl = require('./tmpl.ejs');
// $$tmpl 是个直接可以渲染数据的函数
$(body).append($$tmpl(data));

  我们直接将这个函数传进去,即:

 var $$tmpl = require(' ./tmpl.ejs '); // 一级模板
var $$recursiveTmpl = require('./recursive.ejs'); // 二级模板:局部递归部分的模板,此时$$tmpl 和 $$recursiveTmpl 都是函数
// $$tmpl 是个直接可以渲染数据的函数
$.get('/api/getlist', function (res) {
// 一级模板渲染使用的数据由数据和二级模板函数组成
var data = {
'list': res.list,
'tmplFn': $$recursiveTmpl
} // 将数据传入,一级模板
$(body).append($$tmpl(data));
})

  js调用的部分就算是完成了,那么模板改怎么写呢?我们看看一级模板—— ./tmpl.ejs

<p>递归list</p>
<div>
<%= tmplFn({"list": list, "tmplFn": tmplFn}) %>
</div>

我们再看看二级递归模板—— ./recursive.ejs

 <ul>
<% list.forEach(function(item){ %>
<li>
<span><%= item.title %></span>
<% if (item.subList && item.subList.length > 0) { %>
<span>有下一级</span>
<%= tmplFn ({"list": item.subList , "tmplFn": tmplFn})%>
<% } else { %>
<span>没有下一级了</span>
<% } %>
</li>
<% } %>
</ul>

还记得递归调用的特点吗?1、调用自身;2、由特定条件的出口

可以看出二级递归模板是哥不断延展的ul > li > ul > li 的树形结构,再想象递归调用的特点。不难看出

<%= tmplFn ({"list": item.subList , "tmplFn": tmplFn})%>

就是不断调用自身的保障,tmplFn就是从最外层的js开始传入的,每次作为属性字段传入,供下一级再次使用。而 if (item.subList && item.subList.length > 0) 就是跳出结束使用的边界条件。

就这样,我们就实现了ejs的递归调用,而且是在ejs功能不全的前端ejs-loader中兼容的递归调用。

												

ejs模版实现递归树形结构渲染的更多相关文章

  1. 「SQL归纳」树形结构表的存储与查询功能的实现——通过路径方法(非递归)

    一.树形结构例子分析: 以360问答页面为例:http://wenda.so.com/c/ 我们通过观察URL,可以明确该页面的数据以树形结构存储,下面三块模块分别为: ①根节点 ②根节点的第一层子节 ...

  2. 递归、嵌套for循环、map集合方式实现树形结构菜单列表查询

    有时候, 我们需要用到菜单列表,但是怎么样去实现一个菜单列表的编写呢,这是一重要的问题. 比如我们需要编写一个树形结构的菜单,那么我们可以使用JQuery的zTree插件:http://www.tre ...

  3. WebGL树形结构的模型渲染流程

    今天和大家分享的是webgl渲染树形结构的流程.用过threejs,babylonjs的同学都知道,一个大模型都是由n个子模型拼装而成的,那么如何依次渲染子模型,以及渲染每个子模型在原生webgl中的 ...

  4. 使用Oracle数据库实现树形结构表的子-父级迭代(递归)查询和删除,通过级联菜单简单举例

    前言: 我们在开发中,常常遇到单表的子-父id级联的表结构,在树形的深度不确定的情况下,一次查询出某个树形结构下的所有具有子-父级关系的数据变得十分困难. 这时,我们使用oracle提供的CONNEC ...

  5. 后台返回平铺数据,如何转换成树形json并渲染树形结构,ant tree 异步加载

    如何后台返回对象数组(平铺式) 1.根据字段标识(板块)获取根节点 ### initTreeData(dataOrg){ var resultArr=dataOrg[0] var secArr=[]; ...

  6. 记一则 Lambda内递归调用方法将集合对象转换成树形结构

    public dynamic GetDepartments(string labID) { List<int> usedIDs = new List<int>(); //缓存已 ...

  7. 在论坛中出现的比较难的sql问题:8(递归问题 树形结构分组)

    原文:在论坛中出现的比较难的sql问题:8(递归问题 树形结构分组) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所以,觉得有必 ...

  8. 树形结构的数据渲染(element-ui&VUE)

    在最开始学习的时候,渲染树形数据没有好好理解. 在实际的运用开发中,彻底的走了一遍树形数据,渲染角色权限的业务逻辑. 首先先发送请求获取全部权限树形结构, 其次发送请求获取当前用户的权限, 最后,通过 ...

  9. javascript通过递归改子节点数据-用于层级深度未知的树形结构

    最近在做这么个需求:树形结构,层级深度未知,一旦某个节点的状态是置灰的话,其所有子节点都要置灰. 方案一(数据库有值):如果数据库里置灰节点的所有子节点,值也都是"置灰",那后台取 ...

随机推荐

  1. input的属性用法介绍

    Input表示Form表单中的一种输入对象,其又随Type类型的不同而分文本输入框,密码输入框,单选/复选框,提交/重置按钮等,下面一一介绍. 1,type=text 输入类型是text,这是我们见的 ...

  2. 读EntityFramework.DynamicFilters源码_心得_示例演示02

    上次对EntityFramework.DynamicFilters整体的项目结构有了一个认识,这次我们就通过阅读说明文档,示例项目,和单元测试,来动手构建一个我们的体验项目,通过对动态过滤器的使用,使 ...

  3. 廖雪峰JavaScript练习题2

    请把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字.输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart'] 肯定有更简单的方法, ...

  4. NOIP2017:逛公园

    Sol 发现\(NOIP2017\)还没\(AK\)??? 赶紧改 考场上明明打出了\(DP\),没时间了,没判环,重点是没初始化数组,爆\(0\) \(TAT\) 先最短路,然后\(f[i][j]\ ...

  5. js权威指南学习笔记(二)表达式与运算符

    1.数组初始化表达式 数组直接量中的列表逗号之间的元素可以省略,这时省略的空位会填充undefined.如:       2 2           1 var arr = [1,,,,,6]; 2 ...

  6. 09_Redis持久化——AOF方式

    [AOF简述] AOF(Append-only) Redis每次接受到一条改变数据的命令时,它会把该命令写到一个AOF文件中(只记录写操作,不记录读操作),当Redis启动时,它通过执行AOF文件中的 ...

  7. JS高级程序设计第三版——变量、作用域和内存问题

    JavaScript变量: 由于JavaScript变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已.由于不存在定义某个变量必须要保存何种数据类型值的规则,变量的值及其数据类型可 ...

  8. Redux学习笔记-基础知识

      Redux概述 是什么:Redux是管理状态的容器,提供可预测的状态管理. 怎么做:Redux主要是用来管理组件或者应用的状态,本质上跟一个事件处理器差不多.通过分发action触发reduce来 ...

  9. maven升级遇到的疑惑

    今天在解决一个问题的时候,由于需要修改maven的client包,按照之前的办法,修改完之后,没有修改版本号,而是直接修改client的代码,之后直接 mvn deploy -e 打包上去了,然后奇怪 ...

  10. 续Html5

    HTML5 规定了一种通过 video 元素来包含视频的标准方法 当前video元素支持的三种视频格式 首先呢,显示视频所需要的是 <video src="movie.ogg" ...