和你编写代码相似,你编写的页面也能够由多个小的片段组合而成,这些小的片段本身也能够由更小的片段构成。这些小片段一般是能够在其他页面反复使用的:有些部分能够用在全部页面,而有些部分是某些页面特定的。本篇介绍怎样使用这些可重用的小模板来构成整个页面。
Includes
到眼下为止的样例,我们仅仅显示了HTML的片段,没有实例显示整个页面。以下我们给出完整的显现产品列表的代码和模板:
1 | def catalog() = Action { |
2 | val products = ProductDAO.list |
3 | Ok(views.html.shop.catalog(products)) |
4 | } |
相应的页面模板app/views/products/catalog.scala.html
1 | @ (products : Seq[Product]) |
2 | <!DOCTYPE html> |
3 | <html> |
4 | <head> |
5 | <title>paperclips.example.com</title> |
6 | <link href = "@routes.Assets.at(" stylesheets/main.css ")" |
7 | rel = "stylesheet" > |
8 | </head> |
9 | <body> |
10 | <div id = "header" > |
11 | <h 1 >Products</h 1 > |
12 | </div> |
13 | <div id = "navigation" > |
14 | <ul> |
15 | <li><a href = "@routes.Application.home" >Home</a></li> |
16 | <li><a href = "@routes.Shop.catalog" >Products</a></li> |
17 | <li><a href = "@routes.Application.contact" >Contact</a></li> |
18 | </ul> |
19 | </div> |
20 | <div id = "content" > |
21 | <h 2 >Products</h 2 > |
22 | <ul class = "products" > |
23 | @ for (product <- products) { |
24 | <li> |
25 | <h 3 > @ product.name</h 3 > |
26 | <p class = "description" > @ product.description</p> |
27 | </li> |
28 | } |
29 | </ul> |
30 | </div> |
31 | <footer> |
32 | <p>Copyright paperclips.example.com</p> |
33 | </footer> |
34 | </body> |
35 | </html> |
这样我们就定义了一个完整的HTML页面,可是我们在当中加入了不少和显示产品列表不直接相关的标记,比方Catalog不须要知道菜单是怎样显示的。页面模块化和重用性不高。
一般来说,一个action方法仅仅应负责终于页面的内容部分。对于非常多站点来说,页头,页脚,导航条在不同页面是通用的,例如以下图:
在这个页面样式中,Header,Navigation,Footer一般是不变的,须要变化的部分是由Page Content指定的部分。
因此我们能够把之前产品列表页面模板中的导航条部分抽取出来单独定义一个navigation页面模板:
views/navigation.scala.html
1 | @ () |
2 | <div id = "navigation" > |
3 | <ul> |
4 | <li><a href = "@routes.Application.home" >Home</a></li> |
5 | <li><a href = "@routes.Shop.catalog" >Catalog</a></li> |
6 | <li><a href = "@routes.Application.contact" >Contact</a></li> |
7 | </ul> |
8 | </div> |
然后改动之前的catelog.scala.html
1 | @ (products : Seq[Product]) |
2 | <!DOCTYPE html> |
3 | <html> |
4 | <head> |
5 | <title>paperclips.example.com</title> |
6 | <link href = "@routes.Assets.at(" stylesheets/main.css ")" |
7 | rel = "stylesheet" > |
8 | </head> |
9 | <body> |
10 | <div id = "header" > |
11 | <h 1 >Products</h 1 > |
12 | </div> |
13 | @ navigation() |
14 | <div id = "content" > |
15 | <h 2 >Products</h 2 > |
16 | <ul class = "products" > |
17 | @ for (product <- products) { |
18 | <li> |
19 | <h 3 > @ product.name</h 3 > |
20 | <p class = "description" > @ product.description</p> |
21 | </li> |
22 | } |
23 | </ul> |
24 | </div> |
25 | <footer> |
26 | <p>Copyright paperclips.example.com</p> |
27 | </footer> |
28 | </body> |
29 | </html> |
这个改动使得我们的页面变得更好,由于Catalog页面无需再知道怎样显示导航条,这样的把部分页面模板抽取出来单独写成一个可反复使用页面模板的方法叫”includes”,而抽取出来的模板叫”include”。
Layouts
前面的include使得我们的页面模板变好了,可是还是有改进的余地。我们能够看到Catelog页面还是显示整个页面,比方HTML DocType,head等等,这部分不应该由Catalog来负责显示。前面页面模板仅仅有div content部分由Catalog来显示:
1 | <h 2 >Products</h 2 > |
2 | <ul class = "products" > |
3 | @ for (product <- products) { |
4 | <li> |
5 | <h 3 > @ product.name</h 3 > |
6 | <p class = "description" > @ product.description</p> |
7 | </li> |
8 | } |
9 | </ul> |
其他部分都应该放在Catalog 模板之外。我们也能够使用之前的include技术,但不是最理想的。假设我们使用”include”技术,那么我们须要另外两个新的模板,一个为Content前面部分的内容,另外一个模板为Content后面部分的内容。这样的方法不是非常好,由于这些内容应该是放在一起的。
幸运的是使用Scala的组合功能,Play支持抽出全部的内容到一个模板中,从catalog.scala.html 模板中抽出全部不应由catalog负责的部分,到一个布局模板:
1 | <!DOCTYPE html> |
2 | <html> |
3 | <head> |
4 | <title>paperclips.example.com</title> |
5 | <link href = "@routes.Assets.at(" stylesheets/main.css ")" |
6 | rel = "stylesheet" > |
7 | </head> |
8 | <body> |
9 | <div id = "header" > |
10 | <h 1 >Products</h 1 > |
11 | </div> |
12 | @ navigation() |
13 | <div id = "content" > |
14 | // Content here |
15 | </div> |
16 | <footer> |
17 | <p>Copyright paperclips.example.com</p> |
18 | </footer> |
19 | </body> |
20 | </html> |
我们把这部分模板存放在app/views/main.scala.html中,要使得这个模板变成能够重用的,我们为它定义一个參数content,其类型为html,改动例如以下:
1 | @ (content : Html) |
2 | <!DOCTYPE html> |
3 | <html> |
4 | <head> |
5 | <title>paperclips.example.com</title> |
6 | <link href = "@routes.Assets.at(" stylesheets/main.css ")" |
7 | rel = "stylesheet" > |
8 | </head> |
9 | <body> |
10 | <div id = "header" > |
11 | <h 1 >Products</h 1 > |
12 | </div> |
13 | @ navigation |
14 | <div id = "content" > |
15 | @ content |
16 | </div> |
17 | <footer> |
18 | <p>Copyright paperclips.example.com</p> |
19 | </footer> |
20 | </body> |
21 | </html> |
使用这个模板如同调用Scala函数类型,views.html.main(content) ,使用这个布局模板,我们改动catelog页面例如以下:
1 | @ (products : Seq[Product]) |
2 | @ main { |
3 | <h 2 >Products</h 2 > |
4 | <ul class = "products" > |
5 | @ for (product <- products) { |
6 | <li> |
7 | <h 3 > @ product.name</h 3 > |
8 | <p class = "description" > @ product.description</p> |
9 | } |
10 | </ul> |
11 | } |
假设有须要,我们能够为main布局模板加入很多其他的參数,比方将title也作为參数,
比方把
1 | @ (content : Html) |
2 | <html> |
3 | <head> |
4 | <title>Paper-clip web shop</title> |
改动成
1 | @ (title : String)(content : Html) |
2 | <html> |
3 | <head> |
4 | <title> @ title</title> |
还能够给參数定义缺省值,比方:
1 | @ (title = "paperclips.example.com" )(content : Html) |
这样改动能够进一步參数化布局模板,通用性更强。
很多其他Play教程请訪问http://www.imobilebbs.com/