5.8 Components — Composing Components(组合组件)

一、概述

当你通过和另外一个组件组合的时候,组件就会真正发挥它们的所有潜能。比如<ul>元素,只有<li>元素是适合作为它的子元素的。如果我们希望同样类型的行为,那么我们就必须组合组件。

就像我们组合HTML元素一样,我们可以以同样的方式用于组件。

app/templates/application.hbs

{{#user-list users=activeUsers sortBy=‘name‘ as |user|}}
  {{user-card user=user}}
{{/user-list}}

二、Component Blocks

组件可以以两种形式被使用,就行一般的HTML元素一样。

1. Inline Form

app/templates/application.hbs

{{user-list users=activeUsers}}

这是基本的非块级的组件,这不提供给用户将自己的内容插入到给定的组件的能力。

2. Block Form

app/templates/application.hbs

{{#user-list users=activeUsers}}
  {{!-- custom template here --}}
{{/user-list}}

(1) 为了组合组件,我们必须使用block形式,但是我们也必须能够我们的组件它使用的是哪种形式。这可以通过使用在组件中的hasBlock属性做到。

app/templates/components/user-list.hbs

{{#if hasBlock}}
  {{yield}}
{{else}}
  <p>No Block Specified</p>
{{/if}}

一旦我们知道组件使用的是block格式,我们可以通过使用{{yield}}辅助器把用户放在block中的任何内容填充到我们希望展现的地方。

(2) {{yield}}辅助器在组件的模板中可以被使用多次,允许我们创建一个像list一样工作的组件,这里我们输出一个标题并且为每个post产生内容。

app/templates/components/post-list.hbs

{{#each posts as |post|}}
  <h3>{{post.title}}</h3>
  <p>{{yield}}</p>
{{/each}}

可像这样被使用:

app/templates/posts.hbs

{{#post-list posts=headlinePosts}}
  Greatest post ever!
{{/post-list}}

生成的HTML:

<div id="ember123" class="ember-view">
  <h3>Tomster goes to town</h3>
  <p>Greatest post ever!</p>
  <h3>Tomster on vacation</h3>
  <p>Greatest post ever!</p>
</div>

但是难道只是使用它一遍又一遍输出相同的东西吗?难道我们不想自定义我们的posts并且展示正确的内容吗?我们当然想。让我们稍微探索一下{{yield}}辅助器。

三、Data Down

1. 为了完成组合性的模板,我们需要向这些模板传递数据。可以使用上面介绍的{{yield}}辅助器来实现。

2. 正如我们在前一节看到的,组件的块中定义的内容将会被生产到组件的layout,{{yield}}就定义在这个地方。除此之外,{{yield}}辅助器也允许我们发送数据通过生产参数回到我们调用的组件的作用域。

{{yield}}
{{yield "post"}}
{{yield item}}
{{yield this "footer"}}

3. 默认的yield不会发送任何数据,但是你可以而提供任意数量的参数。此外,所得到的值是按顺序排列的,由于组件暴露它们所以你可以以相同的顺序获取它们。发送数据允许组件的用户使用这个数据。对于用户去获取数据,他需要知道组件暴露了什么数据,这就是文档如此重要,并且从那里产生的数据可以用操作符来访问。让我们看一个例子:

app/components/user-list/template.hbs

{{#each users as |user|}}
  {{yield user}}
{{/each}}

这个{{user-list}}组件会像它的用户一样被产生多次。我们可以通用下面的方法来消除它。

app/users/template.hbs

{{#user-list users=users as |user|}}
  {{user-card user=user}}
{{/user-list}}

现在{{user-card}}获取到当前用户,因为{{yield user}}是在{{each}}块内,当{{user-list}}遍历每个用户时发生改变。虽然这很好,如果我们使用我们的组件不包含一个block,例如{{user-list users=users}}会怎样呢?这样会使组件近乎无用因为没有产生任何东西,但是users仍然被遍历。

4. 让我们混合hasBlock属性并且看是否我们可以使它更有用。

app/components/user-list/template.hbs

{{#if hasBlock}}
  {{#each users as |user|}}
    {{yield user}}
  {{/each}}
{{else}}
  {{#each users as |user|}}
    <section class="user-info">
      <h3>{{user.name}}</h3>
      <p>{{user.bio}}</p>
    </section>
  {{/each}}
{{/if}}

这使得我们的组件更有用,因为它有正常的默认值,但它也允许我们重写这些默认值。通过使用{{yield}}我们可以为用户传递我们的参数。这使得传递数据的概念非常有用。

四、{{compoonent}} Help

1. 为了理解{{component}}辅助器,我们将为{{user-list}}增加下面的新功能:

  • 对每个类型的用于,我们希望使用不同的layout。
  • 这个列表也可以在基本和详细模式间切换。

2. 添加一个新类型的用户,一个超级用户,他将在我们的引用程序中有不同的行为。怎样使用新数据为不同类型的去展现不同的UI呢?这就是{{component}}辅助器将要展示的。这个辅助器让我们能够在运行时选择一个组件。

3. 因为我们有两种用户,‘public‘和‘superuser‘,并且我们希望为每一个类型渲染一个组件加上简单和详细两种模式。我们有下面这些组件名:

  • basic-card-public
  • basic-card-superuser
  • detailed-card-public
  • detailed-card-superuser

我们可以创建这些名字通过使用嵌套形式的{{contact}}辅助器:

{{component (concat ‘basic-card-‘ user.type)}}

现在我们的组件名字是有效的,因为使用了破折号,我们可以把两种模式放在一起组成一个完整的模板。

app/users/template.hbs

{{#user-list users=users as |user basicMode|}}
  {{#if basicMode}}
    {{component (concat ‘basic-card-‘ user.type) user=user}}
  {{else}}
    {{component (concat ‘detailed-card-‘ user.type) user=user}}
  {{/if}}
{{/user-list}}
  • 现在我们根据user.type等于‘superuser‘和‘public‘把{{basic-card-superuser}} {{detailed-card-public}}加载到相应的位置。
  • 使用嵌套在form中的‘concat‘辅助器允许我们这样实现。
  • 嵌套的辅助器在父辅助器前进行评估,然后该辅助器可以使用嵌套的辅助器返回的值。
  • 在这种情况下,只是连续的两个值。你还可以在嵌套的form中实验其他的辅助器,比如if辅助器。

4. 除了使用{{component}},对于basic/detailed模式的功能我们也有第二个产生的值,我们已经添加到yield中,例如{{yield user basic}}

5. 用户可以决定如何命名产生的值。我们可以像这样命名:

app/users/template.hbs

{{#user-list users=users as |userModel isBasic|}}
  {{! something creative here }}
{{/user-list}}

五、Actions Up

1. 现在我们知道了如何发送数据,我们可能想通过一些用户交互来处理数据,比如改变用户的头像。我们可以通过actions来完成这个。

2. 我们将会查找一个{{user-profile}}组件去实现一个save action。通过产生action作为一个参数,允许用户在不需要如何保存的情况下使用组件,但是依旧能触发save。

app/components/user-profile/component.js

import Ember from ‘ember‘;

export default Ember.Component.extend({
  actions: {
    saveUser() {
      var user = this.get(‘user‘);

      user.save()
        .then(() => {
          this.set(‘saved‘, true);
        })
        .catch(error => {
          this.set(‘saveError‘, error);
        });
    }
  }
});

app/components/user-profile/template.hbs

{{! most likely we have some markup here }}
{{yield profile (action "saveUser")}}

3. 所以现在用户将会获取我们定义的data和action。让我们看一下组件是如果作为块的形式被消耗的。

app/templates/user/profile.hbs

{{#user-profile user=user as |profile saveUser|}}
  {{user-avatar url=profile.imageUrl onchange=saveUser}}
{{/user-profile}}

可以从这个例子中看出,用户可以可以hook到{{user-profile}}组件的saveUser action,它被我们嵌入的辅助器(action "saveUser")传递。这个辅助器读取一个不在当前上下文的actions hash中的属性,然后通过这个函数创建一个闭包并且传递参数。

4. 我们也可以利用这个格式把action放在本机HTML元素(如输入按钮):

{{#user-profile user=user as |profile saveUser|}}
  <button type="button" onclick={{action saveUser}}>Save</button>
{{/user-profile}}

这是非常有用的,因为我们重用这些元素提供的 evetn hooks。这使得组件更加重用,因为我们可以组合小的组件更好的做一件事。

六、Wrapping Up

组合组件是关于分割功能为可重用的块,这样可以更容易的退出并且更容易的结合在一起,以便与他们一起很好的工作。这也可以被共容易的测试,因为我们试着原理做所有事的单片组件,我能把功能分割为小的部分。

时间: 2024-10-28 10:49:08

5.8 Components — Composing Components(组合组件)的相关文章

【原创】开源.NET排列组合组件KwCombinatorics使用(三)——笛卡尔积组合

你还可以参考本博客其他.NET开源项目的相关文章: [原创]彩票预测算法:离散型马尔可夫链模型          Newlife XCode组件资源目录汇总[2013年版] [原创]开源.NET下的XML数据库介绍及入门          [原创].NET开源压缩组件介绍与入门 [开源].NET开源表达式计算组件介绍与使用          [原创]开源Word读写组件DocX介绍与入门 [原创]Matlab.NET混编调用Figure窗体                [原创]Matlab与.

【原创】开源.NET排列组合组件KwCombinatorics使用(二)——排列生成

你还可以参考本博客其他.NET开源项目的相关文章: [原创]彩票预测算法:离散型马尔可夫链模型          Newlife XCode组件资源目录汇总[2013年版] [原创]开源.NET下的XML数据库介绍及入门          [原创].NET开源压缩组件介绍与入门 [开源].NET开源表达式计算组件介绍与使用          [原创]开源Word读写组件DocX介绍与入门 [原创]Matlab.NET混编调用Figure窗体                [原创]Matlab与.

组合组件

package com.example.ceshi; import android.app.Activity; import android.content.Context; import android.opengl.Visibility; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; im

【原创】开源.NET排列组合组件KwCombinatorics使用(一)——生成组合序列

你还可以参考本博客其他.NET开源项目的相关文章: [原创]彩票预测算法:离散型马尔可夫链模型          Newlife XCode组件资源目录汇总[2013年版] [原创]开源.NET下的XML数据库介绍及入门          [原创].NET开源压缩组件介绍与入门 [开源].NET开源表达式计算组件介绍与使用          [原创]开源Word读写组件DocX介绍与入门 [原创]Matlab.NET混编调用Figure窗体                [原创]Matlab与.

hibernate联合主键 组合组件(component)

package db.domain; /** * PersonId entity. @author MyEclipse Persistence Tools */ public class PersonId implements java.io.Serializable { // Fields private String firstname; private String lastname; // Constructors /** default constructor */ public Pe

Lightning Web Components 组合(五)

使用组合我们可以用来设计复杂的组件. 组合一些比较小的组件,可以增加组件的重新性以及可维护性. 通过以下一个简单的demo,将会展示关于owner 以及container 的概念,在实际的项目中 example-todo-item 一般是通过for:each 循环动态填充的 <!-- todoApp.html --> <template> <example-todo-wrapper> <example-todo-item item-name="Milk&

【翻译】为什么web components 如此重要

原文链接:https://blog.revillweb.com/why-web-components-are-so-important-66ad0bd4807a#.gq0m0tt0q 这几年,关于 web components 的争论一直不绝于耳.有人说 web components 可以改变我们构建网页的方式,这是为什么呢,是什么让 web components 如此重要? web component 是一种创建封装的.可复用的网页UI (user interface) 组件的标准化方式. 不

前端应该知道的Web Components

前端组件化的痛点 在前端组件化横行的今天,确实极大的提升了开发效率.不过有一个问题不得不被重视,拟引入的这些html.css.js代码有可能对你的其他代码造成影响.虽然我们可以通过命名空间.闭包等一系列措施来加以防备,不过还是存在这些隐患.为了解决这些问题,有一个基本大家遗忘的技术还是可以了解一下的,那就是Web Components. Web Components 是什么 Web Components是一个浏览器的新功能,提供了一个面向web包括下面几个方面标准的组件模型. 你可以认为Web

.NET手记-Autofac进阶Registering Components

通过创建ContainerBuilder并配置暴露的service(接口或者类型)来使用Autofac注册我们的组件. 组件(Components) 可以通过反射, 提供对象实例化,或者通过lambda表达式来创建. ContainerBuilder有一系列的Register()方法来实现组件的注册. ContainerBuilder中每个组件都能通过As()方法来暴露他们一个或多个service. // Create the builder with which components/serv