页面组件
Notice that we called the component to be rendered Lists_show_page
(rather than Lists_show
). This indicates that this template is rendered directly by a Flow Router action and forms the 'top' of the rendering hierarchy for this URL.
请注意,我们称被渲染的组件为Lists_show_page
(而不是List_show
)。
这表明模板根据Flow Router的动作直接渲染而且针对这个URL呈现“顶层”的渲染层级。
The Lists_show_page
template renders without arguments---it is this template's responsibility to collect information from the current route, and then pass this information down into its child templates. Correspondingly the Lists_show_page
template is very tied to the route that rendered it, and so it needs to be a smart component. See the article on UI/UX for more about smart and reusable components.
Lists_show_page
模板渲染 并不需要 参数——收集当前路由的信息然后传递这些信息至子模板是当前模板的职责。相对的,Lists_show_page
模板非常依赖于渲染它的路由,所以它需要是一个非常灵活的组件。在UI/UX相关文章中了解更多关于灵活及可重用组件的相关内容。
It makes sense for a "page" smart component like Lists_show_page
to:
- 收集路由的信息,
- 订阅相关的信息,
- 从这些订阅中取数据,
- 以及向子组件传递数据。
在这个例子中,Lists_show_page
的HTML模板将看起来非常简单,大部分的逻辑包含在JavaScript代码中:
<template name="Lists_show_page">
{{#each listId in listIdArray}}
{{> Lists_show (listArgs listId)}}
{{else}}
{{> App_notFound}}
{{/each}}
</template>
({{#each listId in listIdArray}}
是一种针对页面之间动态渲染的技术)。
Template.Lists_show_page.helpers({
// We use #each on an array of one item so that the "list" template is
// removed and a new copy is added when changing lists, which is
// important for animation purposes.
listIdArray() {
const instance = Template.instance();
const listId = instance.getListId();
return Lists.findOne(listId) ? [listId] : [];
},
listArgs(listId) {
const instance = Template.instance();
return {
todosReady: instance.subscriptionsReady(),
// We pass `list` (which contains the full list, with all fields, as a function
// because we want to control reactivity. When you check a todo item, the
// `list.incompleteCount` changes. If we didn't do this the entire list would
// re-render whenever you checked an item. By isolating the reactiviy on the list
// to the area that cares about it, we stop it from happening.
list() {
return Lists.findOne(listId);
},
// By finding the list with only the `_id` field set, we don't create a dependency on the
// `list.incompleteCount`, and avoid re-rendering the todos when it changes
todos: Lists.findOne(listId, {fields: {_id: true}}).todos()
};
}
});
It's the listShow
component (a reusuable component) that actually handles the job of rendering the content of the page. As the page component is passing the arguments into the reusuable component, it is able to be quite mechanical and the concerns of talking to the router and rendering the page have been separated.
这就是ListShow
组件(一个可服用的组件)实际操作了页面渲染内容的工作。当页面组件向服用组件传递参数时,这将能够非常机械的执行,并且路由器与渲染页面的交流关注点会被分离。
当用户登出时改变页面
There are types of rendering logic that appear related to the route but which also seem related to user interface rendering. A classic example is authorization; for instance, you may want to render a login form for some subset of your pages if the user is not yet logged in.
It's best to keep all logic around what to render in the component hierarchy (i.e. the tree of rendered components). So this authorization should happen inside a component. Suppose we wanted to add this to the Lists_show_page
we were looking at above. We could do something like:
<template name="Lists_show_page">
{{#if currentUser}}
{{#each listId in listIdArray}}
{{> Lists_show (listArgs listId)}}
{{else}}
{{> App_notFound}}
{{/each}}
{{else}}
Please log in to edit posts.
{{/if}}
</template>
Of course, we might find that we need to share this functionality between multiple pages of our app that require access control. We can easily share functionality between templates by wrapping them in a wrapper "layout" component which includes the behavior we want.
You can create wrapper components by using the "template as block helper" ability of Blaze (see the Blaze Article). Here's how we could write an authorization template: 你可以使用Blaze中的“模板块帮助器”功能(查看Blaze相关文章)创建包装器组件。来看看我们是如何编写一个验证模板的:
<template name="App_forceLoggedIn">
{{#if currentUser}}
{{> Template.contentBlock}}
{{else}}
Please log in see this page.
{{/if}}
</template>
Once that template exists, we can simply wrap our Lists_show_page
:
<template name="Lists_show_page">
{{#App_forceLoggedIn}}
{{#each listId in listIdArray}}
{{> Lists_show (listArgs listId)}}
{{else}}
{{> App_notFound}}
{{/each}}
{{/App_forceLoggedIn}}
</template>
The main advantage of this approach is that it is immediately clear when viewing the Lists_show_page
what behavior will occur when a user visits the page.
Multiple behaviors of this type can be composed by wrapping a template in multiple wrappers, or creating a meta-wrapper that combines multiple wrapper templates.