翻译 - 【Dojo Tutorials】Layout with Dijit

原文:Layout with Dijit

在任意图形用户界面上创建动态可交互的布局是一项挑战。使用HTML与CSS创建布局需要我们具备很多的能力。CSS叶落散尽,Dojo创建了一些可扩展的挂件作为Dijit的一部分-Dojo的UI框架。本教程讲解的是,使用Dijit创建布局需要些什么以及如何利用一些挂件轻松的构建复杂的布局。

初探布局管理

CSS是布局语言?为什么又要使用JavaScript与挂件来解决布局问题呢?

 布局挂件没有替代CSS的一般功能,如在页面中定位与浮动内容。相反,它们允许精确的定位与管理我们想要处理的页面区域:

  • 响应缩放事件
  • 为用户控制布局及如何合理布置空间提供支持
  • 为当前的横竖空间自适应控制元素与内容

布局管理是页面加载完后活动控制布局的过程,响应与传播页面上驱动布局的事件。在Dijit中布局是由专门的挂件来完成的。这些挂件主要扮演一个或更多内容或子挂件容器的角色,其控制尺寸与展示子元素。

开始

你可以管理整个页面的布局,也可以只管理其一部分。对于这份教程,我们将开发类桌面应用的布局,一些控制元素与内容固定在页面上。看起来就像这样:

Dijit为开发这样布局的需要提供了一个灵活挂件的集合。我们使用HTML与CSS先准备好场地,然后介绍利用挂件搭建一个典型布局:

 1 <!DOCTYPE HTML>
 2 <html lang="en">
 3     <head>
 4         <meta charset="utf-8">
 5         <title>Demo: Layout with Dijit</title>
 6         <link rel="stylesheet" href="style.css" media="screen">
 7         <link rel="stylesheet" href="dijit/themes/claro/claro.css" media="screen">
 8     </head>
 9     <body class="claro">
10         <div id="appLayout" class="demoLayout">
11             <div class="centerPanel">
12                 <div>
13                     <h4>Group 1 Content</h4>
14                     <p>Lorem ipsum dolor sit amet,...</p>
15                 </div>
16                 <div>
17                     <h4>Group 2 Content</h4>
18                 </div>
19                 <div>
20                     <h4>Group 3 Content</h4>
21                 </div>
22             </div>
23             <div class="edgePanel">Header content (top)</div>
24             <div class="leftCol" class="edgePanel">Siderbar content (left)</div>
25         </div>
26         <script src="dojo/dojo.js" data-dojo-config="async: 1, parseOnLoad: 1"></script>
27     </body>
28 </html>

 

我们有了顶部,边栏,中央内容的DIV包裹器,获取Dojo的script标签也放置好了。在head标签中我们载入了claro主题样式表和我们页面的样式表。在body标签上,为了应用claro的主题,为其添加类claro是必须的,这点经常容易被忽略。

我们需要为布局定几条样式规则:

 1 html, body {
 2     height: 100%;
 3     margin: 0;
 4     overflow: hidden;
 5     padding: 0;
 6 }
 7 
 8 #appLayout {
 9     height: 100%;
10 }
11 
12 #leftCol {
13     width: 14em;
14 }
15 
16 .claro .demoLayout .edgePanel {
17     background-color: #d0e9fc;
18 }
19 
20 #viewsChart {
21     width: 550px;
22     height: 500px;
23 }

 为了得到想要布局与区域内容的行为,我们想要让布局充满视口。我们把文档和最外层的标签设置高度100%。overflow: hidden;是不想使用document的滚动条;滚动将在布局不同的区域中,需要的时候发生。我们给左边的DIV使用em来定义宽度。其他固定的内容将从初始化内容中获取尺寸。

  

 添加挂件

未来实现布局,我们需要Dijit的三个挂件类:dijit/layout/BorderContainer,dijit/layout/TabContainer和dijit/layout/ContentPane。你可以在Dojo工具集“功能”页面查看Dijit挂件的样例。

首先,调用require载入依赖包:

1 <script src="dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: true"></script>
2 <script>
3     require(['dojo/parser', 'dijit/layout/BorderContainer', 'dijit/layout/TabContainer', 'dijit/layout/ContentPane']);
4 </script>

 

注意我们在Dojo的script标签中设置了data-dojo-config属性parseOnLoad为true。这就告诉Dojo当发现挂件化的元素时自动解析。我们完全依赖解析器,甚至都不需要dojo/domReady!之类的,载入完了直接使用。

注意我们需要载入dojo/parser模块。这是非常重要的,尽管经常被忽略,即便是在配置将parseOnLoad设置为了true,dojo/parser也不会自动载入的,以后也不会。版本1.7之前可以不是这样的,由于很多挂件载入dijie/_Templated(它将载入dojo/parser)。

挂件类会在后台载入,且解析器将遍历DOM树。但是实际上接着什么也不会发生了——我们需要创建布局挂件。

例如,我们使用标记或描述的方式实例化挂件。标记元素中的data-dojo-属性为解析器提供挂件的种类与实例化需要的属性配置:

 1 <body class="claro">
 2     <div
 3         id="appLayout"
 4         class="demoLayout"
 5         data-dojo-type="dijit/layout/BorderContainer"
 6         data-dojo-props="design: 'headline'">
 7         <div
 8             class="centerPanel"
 9             data-dojo-type="dijit/layout/ContentPane"
10             dojo-dojo-props="region: 'center'">
11             <div>
12                 <h4>Group 1 Content</h4>
13                 <p>Lorem ipsum dolor sit amet, consectetur ad...</p>
14             </div>
15             <div>
16                 <h4>Group 2 Content</h4>
17             </div>
18             <div>
19                 <h4>Group 3 Content</h4>
20             </div>
21         </div>
22         <div 
23             class="edgePanel"
24             data-dojo-type="dijit/layout/ContentPane"
25             data-dojo-props="region: 'top'">Header content (top)</div>
26         <div
27             id="leftCol"
28             class="edgePanel"
29             data-dojo-type="dijit/layout/ContentPane"
30             data-dojo-props="region: 'left', splitter: true">Sidebar content (left)</div>
31     </div>
32 </body>

 

外层的appLayout元素被配置为BorderContainer,它内部的DIV都是ContentPane。这样就呈现了一个满屏灵活的布局。试着改变demo窗口的大小,看看左边的区域是如何固定的,中间与右边区域会自适应大小。你可能也注意到了左边与中间区域之间竖向的控制手柄,可以自由的调整相对宽度。

这就是我们所谓的动态的灵活的布局。接下来我们将在demo中添加tab面板,但是首先让我们回过头来了解一下分隔布局挂件及他们的用法。

边框容器

 如果你在其他GUI工具中使用过布局管理器与容器,dijit/layout/BorderContainer对你来说应该不陌生,and if not we can soon catch you up。

边框容器允许你定义一个细分为区域的布局。当其他区域都固定尺寸后:"top", "bottom", "leading", "trailing", "left"和"right",中间区域总是灵活的自适应大小。

所有的Dijit挂件支持国际化,所以Dijit不能臆断页面上的内容和控制元素是从左至右的。针对从左至右的地区,leading部分将在左边,trailing部分在右边。针对从右至左的地区(如:阿拉伯语,希伯来语),是相反的。你可以使用left和right确保内容在你想要放置的一边,不管是什么地域。全依赖与内容的逻辑。

正如我们在示例中看到的,每个区域都作为子挂件呈现。Dijit的挂件都支持region属性,所以原则上,你可以使用任何挂件,尽管有些确实比其他工作的更好。固定的区域可以通过设置splitter属性显示一个用户可移动的分割器。

当使用边框容器时,区域的尺寸是通过CSS指定的——使用样式表或行内样式。注意尽管你可以使用百分比来初始化尺寸,但在渲染时会被转化为像素。所以不能使用百分比的值来定义边框容器的尺寸。中心区域不应该设置高度或宽度;它会自动填充剩余空间。

我们建立的布局,所有的区域都是ContentPane——都是出于载入内容和作为挂件内容容器的目的,但是在第一个布局中心的的TabContainer区域,就不是这样的。实际上,边框容器经常嵌套使用。下面是一个更复杂布局嵌套使用边框容器的例子:

 1 <div class="demoLayout" style="height: 300px; width: 300px" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design: 'headline'">
 2     <div class="centerPanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">center</div>
 3     <div class="demoLayout" style="height: 50%" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="region: 'top', splitter: true, design: 'headline'">
 4         <div class="centerPanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">center</div>
 5         <div class="edgePanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'bottom'">bottom</div>
 6     </div>
 7     <div class="edgePanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="splitter: true, region: 'left'">left</div>
 8     <div class="demoLayout" style="width: 50%" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="region: 'right', design: 'headline'">
 9         <div class="centerPanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">center</div>
10         <div class="edgePanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'bottom'">left</div>
11     </div>
12     <div class="edgePanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="splitter: true, region: 'bottom'">bottom</div>
13 </div>

关于边框容器的更新细节请参考:边框容器文档

创建Tab

一个布局挂件的职责就是布局与在其有效区域内展示其内容。很多时候它们的内容是一个或多个挂件。一个常规的需求就是一次只展示其一个子挂件,把它们作为堆栈,用户可以移动它们。最大利用空间,还允许只有在挂件被选中时才载入其内容。Dijit提供了解决方案,使用StackContainer,TabContainer和AccordionContainer。

布局要创建作为选项卡式的面板拥有不同组的div,在中心区域的底部有个选项卡条。这是一个模拟文件系统的常规而直观的UI模式。dijit/layout/TabContainer挂件实现了这个模式。它表现为包含选项卡条的子挂件,再剩余空间内展示选项卡对应的内容。

 1 <div class="centerPanel"
 2     data-dojo-type="dijit/layout/TabContainer"
 3     data-dojo-props="region: 'center', tabPosition: 'bottom'">
 4     <div
 5         data-dojo-type="dijit/layout/ContentPane"
 6         data-dojo-props="title: 'Group 1'">
 7         <h4>Group 1 Content</h4>
 8         <p>Lorem ...</p>
 9     </div>
10     <div
11         data-dojo-type="dijit/layout/ContentPane"
12         data-dojo-props="title: 'Group Two'">
13         <h4>Group 2 Content</h4>
14     </div>
15     <div
16         data-dojo-type="dijit/layout/ContentPane"
17         data-dojo-props="title: 'Long Tab Label for this One'">
18         <h4>Group 3 Content</h4>
19     </div>
20 </div>

 

为了在TabContainer中展示“group”的部分,我们先创建一个容器元素TabContainer。这个挂件本身就是BorderContainer的一个子元素,所以依然需要region属性。TabContainer为选项卡与其内容如何展示提供了一些配置选项;我们通过设置tabPosition属性将选项卡条放置在底部。TabContainer也是一个容器挂件——它管理其子挂件——所以我们需要将适当的片段块包裹其中。它们不需要有奇幻的功能,ContentPane就是不错的选择。注意它们都提供了一个title属性。title就是选项卡条中与其内容对应的标签。

StackContainer与它的朋友们

我们的布局完成了,但是如何灵活运用建立属于自己的布局,甚至是自己的布局挂件,我们需要更深入的学习。TabContainer实际上是dijit/layout/StackContainer的一个子类;汲取了StackContainer的很多功能(StackContainer继承自dijit/layout/_LayoutWidget),TabContainer特有的是如何排列与呈现内容面板,StackContainer是一个通用挂件。没有固有的控制元素来导航子挂件,但是dijit/layout/StackContainer作为一个简单的例子还是可用的。下图就是我们将TabContainer换成StackContainer的样子,我们在底部添加了一些控制挂件。为了让一切整齐,我们将边栏也使用了BorderContainer。

Application Layout with StackContainer

Dijit还提供了dijit/layout/AccordionContainer使下拉变得容易,且在dojox/layout包中还有其他的StackContainer的子类也许能满足你的需求。通常可使用dojox/layout/ExpandoPane替换ContentPane以获得一个可折叠的面板。一般情况下,在你想要自定义之前应当先看看这些已有的挂件提供的配置选项,也许已满足了你的需求。

启动与调整大小

目前为止我们以标记的方式欢快地搭建了布局,且Dojo的parser会根据标记按序实例化挂件。真正的理解dijit的布局还有很长的路要走。如果你需要以程序的方式新建并插入挂件,巫术帮不了我们。我们需要了解一下布局是何时及如何发生的。

让我们回顾一下,我们已知了:

  • 根据我们定义的步骤按序创建挂件
  • 布局本质上连接着有效空间的测量
  • 知道startup调用挂件的domNode才接入到DOM中
  • 布局挂件灵活的布置它的子挂件

当我们以程序的方式创建挂件,我们必须完成顺序由调用startup方法。这一步包含任何事情可以值发生一次一个挂件是在DOM中-包含测量与大小。布局挂件在其子挂件调用startup,所以启动事件从高层传递了下去。

默认的,所有的布局挂件都用个resize方法。这个方法在启动时候调用,且在改变尺寸(如改变窗口大小)或添加子挂件的时候也会调用。想startup一样,resize也会向下传递,允许每个挂件在布局自适应大小,传递新的尺寸给其子挂件。

让我们来看看代码如何实现:

 1 <head>
 2     <script src="dojo/dojo.js" data-dojo-config="async: 1"></script>
 3     <script>
 4         require([
 5             'dijit/registry',
 6             'dijit/layout/BorderContainer',
 7             'dijit/layout/ContentPane',
 8             'dijit/layout/TabContainer',
 9             'dojo/domReady!'
10         ], function(registry, BorderContainer, ContentPane, TabContainer) {
11       // ... 
12 
13         });
14     </script>
15 </head>
16 <body class="claro">
17     <div id="appLayout" class="demoLayout"></div>
18 </body>

我们省略了parseOnLoad,默认为false;我们使用了dojo/domReady!等待DOM载入完成。

 1 // create the BorderContainer and attach it to our appLayout div
 2 var appLayout = new BorderContainer({
 3     design: 'headline'
 4 }, 'appLayout');
 5 
 6 // create the TabContainer
 7 var contentTabs = new TabContainer({
 8     region: 'center',
 9     id: 'contentTabs',
10     tabPosition: 'bottom',
11     'class': 'centerPanel'
12 });
13 
14 // add the TabContainer as a child of the BorderContainer
15 appLayout.addChild(contentTabs);
16 
17 // create and add the BorderContainer edge regions
18 appLayout.addChild(
19     new ContentPane({
20         region: 'top',
21         'class': 'edgePanel',
22         content: 'Header content (top)'
23     })
24 );
25 appLayout.addChild(
26     new ContentPane({
27         region: 'left',
28         id: 'leftCol',
29         'class': 'edgePanel',
30         content: 'Sidebar content (left)',
31         splitter: true
32     })
33 );
34 
35 // add initial content to the TabContainer
36 contentTabs.addChild(
37     new ContentPane({
38         href: 'contentGroup1.html',
39         title: 'Group 1'
40     })
41 );
42 
43 // start up and do layout
44 appLayout.startup();

 

每个挂件实例化的参数与之前在data-dojo-props中配置的一样。相对与标记那种隐式的包含关系,每个挂件都被使用addChild添加到了父元素下。

注意在挂件都添加了之后才调用的appLayout的startup方法。直到调用了startup,addChild只是将挂件注册为子元素。在启动以后,addChild才呈现在布局中,在父元素上自动触发resize,其子挂件也是如此。

我们可以实际添加一个子元素测试下上面所讲:

1 function addTab(name) {
2     var pane = new ContentPane({
3         title: name,
4         content: '<h4>' + name + '</h4>'
5     });
6 
7     // add the new  pane to our contentTabs widget
8     registry.byId('contentTabs').addChild(pane);
9 }

 

总结

我们学会了如何使用Dijit提供的组件构建动态布局的方法,一种是使用标记,另一种是以编程的方式。该方法允许你通过配置选项定义和组合你的UI。随着我们探索更多的Dijit你会发现同样的灵活性。我们可以增加我们自己的选项来定义属于自己的挂件,创建Dijit的基础内容。这个后面教程要探讨的专题。

 

posted on 2014-09-26 14:44  古道倦马  阅读(603)  评论(0编辑  收藏  举报

导航