我将构建具有许多不同视图的复杂应用程序。想象一下例如eshop解决方案。可以有很多不同的观点:
现在我有点困惑,如何使用Web UI构建这样一个复杂的应用程序。我希望将视图的HTML模板分隔成多个文件并使用一些逻辑来确定哪一个应该呈现。
假设我想要一个包含页眉和页脚等基本内容的主模板,那么我有很多内容模板,这些模板应该被注入主模板内的正确位置。
到目前为止,我一直只看到使用Dart Web UI的小型单模板示例,所以我不知道如何实现这一点。
答案 0 :(得分:27)
我已经汇总了我目前如何做的一个小例子(希望我们很快会看到一个更大的最佳实践示例应用程序):
有关此示例的完整源代码,请参阅gist: How to build a Web UI application with multiple views in Dart
instantiate Web Components的标准方法是在 HTML 中使用<x-foo></x-foo>
。
由于我们有不同的观点,我们必须在 Dart 代码中实例化Web组件。这样做我们必须手动调用Web Components生命周期方法。这不是直截了当的,将来可能会有所改进(参见Issue 93,其中也包含一些例外)。
以下是切换视图的方式(app.dart
的来源):
import 'dart:html';
import 'package:web_ui/web_ui.dart';
import 'contact.dart';
import 'products.dart';
void main() {
// Add view navigation event handlers
query('#show-contact-button').onClick.listen(showContactView);
query('#show-products-button').onClick.listen(showProductView);
}
// Used to call lifecycle methods on the current view
ComponentItem lifecycleCaller;
/// Switches to contacts view
void showContactView(Event e) {
removeCurrentView();
ContactView contactView = new ContactView()
..host = new Element.html('<contact-view></contact-view>');
lifecycleCaller = new ComponentItem(contactView)..create();
query('#view-container').children.add(contactView.host);
lifecycleCaller.insert();
}
/// Switches to products view
void showProductView(Event e) {
removeCurrentView();
ProductsView productsView = new ProductsView()
..host = new Element.html('<products-view></products-view>');
lifecycleCaller = new ComponentItem(productsView);
lifecycleCaller.create();
query('#view-container').children.add(productsView.host);
lifecycleCaller.insert();
}
void removeCurrentView() {
query('#view-container').children.clear();
if (lifecycleCaller != null) {
// Call the lifecycle method in case the component needs to do some clean up
lifecycleCaller.remove();
}
}
以下是app.html
的来源:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>A Complex Web UI Application</title>
<link rel="stylesheet" href="app.css">
<!-- import the header and footer components -->
<link rel="components" href="header.html">
<link rel="components" href="footer.html">
<!-- import the view components -->
<link rel="components" href="contact.html">
<link rel="components" href="products.html">
</head>
<body>
<header-component></header-component>
<div id="view-container"></div>
<button id="show-contact-button">show contact view</button>
<button id="show-products-button">show products view</button>
<footer-component></footer-component>
<script type="application/dart" src="app.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
注意:我必须通过<link rel="components" href="contact.html">
导入视图组件,即使我没有在HTML文件中直接引用它。
答案 1 :(得分:16)
您可以将route库与模板结合使用,从而大大自动化该过程。
在urls.dart
中,您将定义应用将处理的路线。 app.dart
将设置路由侦听器。最后,app.html
将保存一个页面容器,它将自动切换页面组件(通过使用模板实例化)。
设置此结构后,可以通过常规锚标记处理页面导航,而不是调用自定义函数来更改页面。
要添加新页面,您必须执行以下操作:
urls.dart
pages/
文件夹app.html
您可以在下面看到处理主页和联系页面的应用示例:
urls.dart:
library urls;
import 'package:route/url_pattern.dart';
final homeUrl = new UrlPattern(r'/');
final contactUrl = new UrlPattern(r'/contact');
app.dart:
import 'dart:html';
import 'package:web_ui/web_ui.dart';
import 'package:route/client.dart';
import 'urls.dart' as urls;
import 'package:web_ui/watcher.dart' as watchers;
// Setup the routes to listen to
void main() {
var router = new Router()
..addHandler(urls.homeUrl, showPage)
..addHandler(urls.contactUrl, showPage)
..listen();
}
// Change the page that we are on
void showPage(String path) {
watchers.dispatch();
}
app.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sample app</title>
<link rel="stylesheet" href="app.css">
<!-- import the pages -->
<link rel="components" href="pages/xhomepage.html">
<link rel="components" href="pages/xcontactpage.html">
</head>
<body>
<!-- You could put a header here if you want !-->
<!-- Templates take care of automatically switching the page !-->
<div class="pages">
<template instantiate="if urls.homeUrl.matches(window.location.pathname)">
<x-home-page></x-home-page>
</template>
<template instantiate="if urls.contactUrl.matches(window.location.pathname)">
<x-contact-page></x-contact-page>
</template>
</div>
<!-- You could put a footer here if you want !-->
<script type="application/dart" src="app.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
编辑:我删除了app.dart必须定义自己的网页的步骤。相反,模板会检查URL路径是否与urls.dart中定义的UrlPattern匹配。这应该简化一些事情。
答案 2 :(得分:2)
我创建了一个Polymer元素<bind-view>
,它根据当前路径创建并添加一个视图元素。该元素与route_hierarchical包一起使用
有关详细信息,请参阅GitHub上的BWU Polymer Routing。
路线配置类似于
library bwu_polymer_routing_example.route_initializer;
import 'package:route_hierarchical/client.dart' as rt;
import 'package:bwu_polymer_routing/module.dart';
class RouteInitializer implements Function {
void call(rt.Router router, RouteViewFactory views) {
views.configure({
'usersList': routeCfg(
path: '/users',
view: 'user-list',
defaultRoute: true,
dontLeaveOnParamChanges: true,
enter: (route) => router.go('usersList', {})),
'user': routeCfg(
path: '/user/:userId',
view: 'user-element',
dontLeaveOnParamChanges: true,
mount: {
'articleList': routeCfg(
path: '/articles',
view: 'article-list',
defaultRoute: true,
dontLeaveOnParamChanges: true,
mount: {
'article': routeCfg(
path: '/article/:articleId',
view: 'article-element',
bindParameters: ['articleId', 'userId'],
dontLeaveOnParamChanges: true,
mount: {
'view': routeCfg(
path: '/view',
defaultRoute: true,
dontLeaveOnParamChanges: true),
'edit': routeCfg(
path: '/edit',
dontLeaveOnParamChanges: true)
})
})
})
});
}
}
<app-element>
包含<bind-view>
元素,占位符,其中添加了为当前路由配置的视图。
视图可以嵌套。任何视图本身都可以包含<bind-view>
元素。这允许创建没有太多样板的分层视图合成。
<!DOCTYPE html>
<link rel='import' href='../../../../packages/polymer/polymer.html'>
<link rel='import' href='../../../../packages/bwu_polymer_routing/bind_view.html'>
<link rel='import' href='user_list.html'>
<link rel='import' href='user_element.html'>
<link rel='import' href='article_list.html'>
<link rel='import' href='article_element.html'>
<polymer-element name='app-element'>
<template>
<bind-view id='app-element'></bind-view>
</template>
<script type='application/dart' src='app_element.dart'></script>
</polymer-element>
app_element.dart
文件包含路由器初始化代码
class AppModule extends Module {
AppModule() : super() {
install(new RoutingModule(usePushState: true));
bindByKey(ROUTE_INITIALIZER_FN_KEY, toValue: new RouteInitializer());
}
}
@CustomTag('app-element')
class AppElement extends PolymerElement with DiContext {
AppElement.created() : super.created();
@override
void attached() {
super.attached();
initDiContext(this, new ModuleInjector([new AppModule()]));
}
}
该软件包还包含一些帮助mixin,以便为Polymer元素添加dependency injection (DI)功能,例如此处使用的DiContext
mixin。
构造函数注入不能与Polymer一起使用,但事件是一个很好的替代品。
DiConsumer
mixin允许使用这个简单的代码从DI请求实例
@CustomTag('article-list')
class ArticleList extends PolymerElement with DiConsumer {
@observable String userId;
@override
void attached() {
super.attached();
// The two lines below show how to request instances from DI
// but they are redundant here because
// route parameters are assigned to attributes of the view automatically
// when the view is created or when the values change
var di = inject(this, [RouteProvider /* add more types here as needed */]);
userId = (di[RouteProvider] as RouteProvider).parameters['userId'];
}
}