学习笔记中断了将近一个月,除了事儿多、工作忙之外,学习的过程也确实让我头疼,因为这次涉及到了lift框架核心的一些东西,LifteSession,LiftServlet等等,我不得不经常停下来再啃几章《Programming In Scala》。过程中着实感触不少:1.没有ide的调试支持,只靠println大法来学习框架,实在太痛苦;2.不好好锻炼一下函数式编程的思维,学习scala/lift实在不是太容易的事情,现在看从java开发者转变到scala的开发者也许没有想象中的容易;3.scala的很多特性也许让写代码变得简单,但是给阅读代码带来的复杂程度也不小;4.满屏幕Type Parameterization 的东西实在让人看着头大。
闲话说了不少了,赶紧把刚刚理清楚的思路记下来。
8.Create An Entry(1)--toForm
创建一个Entry的过程可以分以下三个过程来理解:定义Entry类,根据类的定义创建Form表单,获取表单提交的信息,写入数据库。我们还是按照看到的顺序来学习设计到的东西。
entry.html->entry()
def entry = (new Entry).author(User.currentUser).toForm(Full("Post"),
(t: Entry) => {
t.save
println("after entry.save")
S.redirectTo("/view?id=" + t.id)
})
首先是author的赋值,这是一个链式调用,赋值返回的结果还是一个entry,我们来看author 的定义。
object author extends MappedLongForeignKey(this, User) {
override def dbDisplay_? = false
}
author 是一个object 而不是一个简单的val,因为我们需要通过重载他所继承的基类的方法来对author的行为(包括约束,展现等)进行定制。
MappedLongForeignKey是MappedField的子类,在MetaMapper的findMagicFields方法实现中我们能看到,lift是通过查找是MappedField实例的属性来确定需要由Mapper框架维护的属性的。
abstract class MappedLongForeignKey[T<:Mapper[T],O<:KeyedMapper[Long, O]](theOwner: T, foreign: => KeyedMetaMapper[Long, O])
extends MappedLong[T](theOwner) with MappedForeignKey[Long,T,O] with BaseForeignKey
abstract class MappedLong[T<:Mapper[T]](val fieldOwner: T) extends MappedField[Long, T]
查看MeppedField的子类,能得到可以用的类型
MappedEnumList, MappedFakeClob, MappedForeignKey, MappedDateTime, MappedText, MappedPassword, MappedDouble, MappedBoolean, MappedBinary, MappedInt, MappedEnum, MappedLong, MappedString
MappedForeignKey相关的内容对现在的过程没有影响,我们以后再来研究。
在MappedField的apply方法中可以看到,在函数的最后会返回fieldOwner,这个fieldOwner就是Entry对象,是通过 extends时的this传入的,这有助于我们理解,为什么每一个属性的定义都要加上个this.
def apply[Q <% FieldType](v: Q): OwnerType = {
this.set(v)
fieldOwner
}
之后是一个toForm的调用
def toForm(button : Box[String], f : (A) => Any)
Present the model as a form and execute the function on submission of the form
param
f - - the function to execute on form submission
button - - If it's Full, put a submit button on the form with the value of the parameter
return
- the form
看他的执行过程之前先来看看这两个参数。button简单,就是显示在submit按钮上的值。f参数就有点神奇了,因为toForm的执行和f的执行之间可隔着两个http的过程呢,还有用户的参与。搞明白这点的过程相当曲折,说白了却也比较简单:在toForm的执行过程中,lift会通过S对象把f保存在一个ThreadGlobal(java中ThreadLocal封装)的变量中,并且把保存的key值通过一个hidden域输出到表单中;当表单提交时,在LiftSession.runParams 方法中会把这个方法取出来执行。具体实现的过程实在是比较复杂,好在我们暂时还不需要彻底搞明白。
(注:其实不止是submit如此,每一个字段都会有这样一个方法保存,通过生成input的name属性来保存关联,估计lift里,这个name属性是不能再被利用了)
这里有还有一点值得说明的是,这个toForm生成的表单的提交地址,就是表单的访问地址。但是这个提交请求在常规的render执行之前先执行了这里的f方法,然后被 S.redirectTo(...)重定向,中止了正常的处理过程。如果去掉 S.redirectTo()的调用,将在数据保存后,看到一个新的空白的表单。
现在可以放下包袱来看Form的输出过程了。
1).Mapper.toForm方法首先通过getSingleton的调用获得到他的伴生对象
2).调用定义在伴生对象的基类KeyedMetaMapper中的toForm方法,逐个生成属性字段需要的表单项
2.1) 依次调用被映射的属性对象,根据属性对象自身的各种属性决定是否调用属性对象的toForm方法,生成表单项(各种input)
2.2) 获取MappedField的displayName 通过formatFormLine方法输出显示名,并加上各种格式代码
3).Mapper.toForm保存onSubmit函数(f),生成submit按钮
我们来看这个过程中几个需要关注的东西
1) 我们必须在class中重载一个getSingleton方法,以找到伴生对象
2) 在属性对象(MappedField)中的一些值可以用来控制表单的显示,比如说author属性中的 dbDisplay_? 如果设为false,则不会显示在表单中
3) 可以通过重载displayName来控制字段的显示名称
4) MappedField的toForm方法会调用_toForm方法来最终决定输出内容,如果想自己控制输出格式,可以考虑重载这个方法,比如说
object title extends MappedString(this, 128) {
override def displayName = "标题"
override def _toForm: Box[Elem] =
fmapFunc({s: List[String] => this.setFromAny(s)}) {
name =>
Full(<div> <input type='text' id={fieldId} maxlength={maxLen.toString}
name={name}
value={is match {case null => "" case s => s.toString}}/>元素级别加点啥呢?</div>)
}
}
5) 如果想在行一级修改缺省输出,可以重新定义KeyedMetaMapper的formatFormElement的方法,注意,formatformElement 是var,不是def, 这里不能用重载。比如说
formatFormElement = (name, form) =>
<xml:group> <tr>
<td>行级别加点啥:{name}</td>
<td>{form}</td>
</tr> </xml:group>
到这儿,form的输出过程就差不多了,但是,还差一点东西,<table/>标签呢?<form/>标签呢?
在这个例子里<table/>直接写在了entry.html中,这也许是我们自己实现中需要注意的东西(btw.如果页面不用table控制咋办?)
<form/>是在LiftSession.processSnippet方法的最后处理的,这里会判断snippet中有没有form="xx" 属性,如果有,则生成一个<form/>来包含snippet返回的内容,xx会输出method=xx
好了,打完收工。
分享到:
相关推荐
Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习笔记Java学习...
2022吴恩达机器学习笔记汇总(共10章节).zip2022吴恩达机器学习笔记汇总(共10章节).zip2022吴恩达机器学习笔记汇总(共10章节).zip2022吴恩达机器学习笔记汇总(共10章节).zip2022吴恩达机器学习笔记汇总(共10章节).zip...
希沃白板学习笔记.pdf希沃白板学习笔记.pdf希沃白板学习笔记.pdf希沃白板学习笔记.pdf希沃白板学习笔记.pdf希沃白板学习笔记.pdf希沃白板学习笔记.pdf希沃白板学习笔记.pdf希沃白板学习笔记.pdf
Springcloud学习笔记.md,Springcloud学习笔记.md,Springcloud学习笔记.md,Springcloud学习笔记.md,Springcloud学习笔记.md,Springcloud学习笔记.md,Springcloud学习笔记.md,Springcloud学习笔记.md,Spring...
人工智能学习笔记,人工智能学习笔记,人工智能学习笔记人工智能学习笔记,人工智能学习笔记,人工智能学习笔记人工智能学习笔记,人工智能学习笔记,人工智能学习笔记人工智能学习笔记,人工智能学习笔记,人工智能...
CCNA学习笔记 CCNA学习笔记 CCNA学习笔记
云的学习笔记-云的学习笔记系统-云的学习笔记系统源码-云的学习笔记管理系统-云的学习笔记管理系统java代码-云的学习笔记系统设计与实现-基于ssm的云的学习笔记系统-基于Web的云的学习笔记系统设计与实现-云的学习...
云的学习笔记-云的学习笔记系统-云的学习笔记系统源码-云的学习笔记管理系统-云的学习笔记管理系统java代码-云的学习笔记系统设计与实现-基于ssm的云的学习笔记系统-基于Web的云的学习笔记系统设计与实现-云的学习...
nginx学习笔记(软件+学习笔记) 仅供学习交流! 后续会持续分享相关资源,记得关注哦! nginx学习笔记(软件+学习笔记) 仅供学习交流! 后续会持续分享相关资源,记得关注哦! nginx学习笔记(软件+学习笔记) ...
docker学习笔记,docker学习笔记,docker学习笔记,docker学习笔记,docker学习笔记,docker学习笔记,docker学习笔记,docker学习笔记,docker学习笔记,docker学习笔记,docker学习笔记,docker学习笔记,docker学习笔记,...
PHP个人学习笔记
Contiki学习笔记:进程、事件、etimer关系 Contiki 实例: Contiki学习笔记:创建两个交互进程 Contiki 主函数剖析: Contiki学习笔记:main函数剖析 Contiki学习笔记:启动一个进程process_start Contiki学习笔记...
SSH学习笔记 SSH学习笔记 SSH学习笔记 SSH学习笔记 SSH学习笔记 SSH学习笔记 SSH学习笔记 SSH学习笔记 SSH学习笔记 SSH学习笔记 SSH学习笔记 SSH学习笔记
ssh学习笔记1 ssh学习笔记1 ssh学习笔记1 ssh学习笔记1 ssh学习笔记1 ssh学习笔记1 ssh学习笔记1